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,263 @@
|
|
|
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
|
+
LOG_FILE="$LOG_DIR/scout.log"
|
|
9
|
+
LOG_LEVEL="${SCOUT_LOG_LEVEL:-INFO}" # DEBUG, INFO, WARN, ERROR
|
|
10
|
+
|
|
11
|
+
# 로그 디렉토리 생성
|
|
12
|
+
mkdir -p "$LOG_DIR"
|
|
13
|
+
|
|
14
|
+
# 로그 레벨 숫자 변환
|
|
15
|
+
get_level_num() {
|
|
16
|
+
case "$1" in
|
|
17
|
+
DEBUG) echo 0 ;;
|
|
18
|
+
INFO) echo 1 ;;
|
|
19
|
+
WARN) echo 2 ;;
|
|
20
|
+
ERROR) echo 3 ;;
|
|
21
|
+
*) echo 1 ;;
|
|
22
|
+
esac
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# 현재 로그 레벨 확인
|
|
26
|
+
should_log() {
|
|
27
|
+
local msg_level="$1"
|
|
28
|
+
local current_level_num=$(get_level_num "$LOG_LEVEL")
|
|
29
|
+
local msg_level_num=$(get_level_num "$msg_level")
|
|
30
|
+
|
|
31
|
+
[ "$msg_level_num" -ge "$current_level_num" ]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# 로그 기록
|
|
35
|
+
log() {
|
|
36
|
+
local level="$1"
|
|
37
|
+
local message="$2"
|
|
38
|
+
local context="$3"
|
|
39
|
+
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
40
|
+
|
|
41
|
+
# 로그 레벨 필터링
|
|
42
|
+
if ! should_log "$level"; then
|
|
43
|
+
return 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# 로그 포맷: [timestamp] [level] message | context
|
|
47
|
+
local log_line="[$timestamp] [$level] $message"
|
|
48
|
+
if [ -n "$context" ]; then
|
|
49
|
+
log_line="$log_line | $context"
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# 파일에 기록
|
|
53
|
+
echo "$log_line" >> "$LOG_FILE"
|
|
54
|
+
|
|
55
|
+
# DEBUG 모드면 stdout에도 출력
|
|
56
|
+
if [ "$SCOUT_DEBUG" = "true" ]; then
|
|
57
|
+
echo "$log_line"
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# 편의 함수들
|
|
62
|
+
log_debug() { log "DEBUG" "$1" "$2"; }
|
|
63
|
+
log_info() { log "INFO" "$1" "$2"; }
|
|
64
|
+
log_warn() { log "WARN" "$1" "$2"; }
|
|
65
|
+
log_error() { log "ERROR" "$1" "$2"; }
|
|
66
|
+
|
|
67
|
+
# 이벤트 로깅 (구조화된 JSON)
|
|
68
|
+
log_event() {
|
|
69
|
+
local event_type="$1"
|
|
70
|
+
local event_data="$2"
|
|
71
|
+
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
72
|
+
local user="${USER:-unknown}"
|
|
73
|
+
local session_id=""
|
|
74
|
+
|
|
75
|
+
# 세션 ID 가져오기
|
|
76
|
+
if [ -f "$DATA_DIR/.session" ] && command -v jq &> /dev/null; then
|
|
77
|
+
session_id=$(jq -r '.sessionId // ""' "$DATA_DIR/.session" 2>/dev/null)
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# JSON 이벤트 로그
|
|
81
|
+
local event_log="$LOG_DIR/events.jsonl"
|
|
82
|
+
echo "{\"timestamp\":\"$timestamp\",\"type\":\"$event_type\",\"user\":\"$user\",\"session\":\"$session_id\",\"data\":$event_data}" >> "$event_log"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# 로그 조회
|
|
86
|
+
view_logs() {
|
|
87
|
+
local lines="${1:-50}"
|
|
88
|
+
local level="${2:-}"
|
|
89
|
+
|
|
90
|
+
if [ ! -f "$LOG_FILE" ]; then
|
|
91
|
+
echo "No logs found"
|
|
92
|
+
return
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
if [ -n "$level" ]; then
|
|
96
|
+
grep "\[$level\]" "$LOG_FILE" | tail -n "$lines"
|
|
97
|
+
else
|
|
98
|
+
tail -n "$lines" "$LOG_FILE"
|
|
99
|
+
fi
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# 로그 검색
|
|
103
|
+
search_logs() {
|
|
104
|
+
local pattern="$1"
|
|
105
|
+
local lines="${2:-20}"
|
|
106
|
+
|
|
107
|
+
if [ ! -f "$LOG_FILE" ]; then
|
|
108
|
+
echo "No logs found"
|
|
109
|
+
return
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
grep -i "$pattern" "$LOG_FILE" | tail -n "$lines"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# 이벤트 조회
|
|
116
|
+
view_events() {
|
|
117
|
+
local count="${1:-20}"
|
|
118
|
+
local event_type="$2"
|
|
119
|
+
local event_log="$LOG_DIR/events.jsonl"
|
|
120
|
+
|
|
121
|
+
if [ ! -f "$event_log" ]; then
|
|
122
|
+
echo "No events found"
|
|
123
|
+
return
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
if [ -n "$event_type" ]; then
|
|
127
|
+
grep "\"type\":\"$event_type\"" "$event_log" | tail -n "$count" | jq -r '"\(.timestamp) [\(.type)] \(.data)"'
|
|
128
|
+
else
|
|
129
|
+
tail -n "$count" "$event_log" | jq -r '"\(.timestamp) [\(.type)] \(.data)"'
|
|
130
|
+
fi
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# 로그 통계
|
|
134
|
+
log_stats() {
|
|
135
|
+
echo "=== Log Statistics ==="
|
|
136
|
+
echo ""
|
|
137
|
+
|
|
138
|
+
if [ -f "$LOG_FILE" ]; then
|
|
139
|
+
echo "Log File: $LOG_FILE"
|
|
140
|
+
echo "Log Level: $LOG_LEVEL"
|
|
141
|
+
echo ""
|
|
142
|
+
echo "By Level:"
|
|
143
|
+
for level in DEBUG INFO WARN ERROR; do
|
|
144
|
+
local count=$(grep -c "\[$level\]" "$LOG_FILE" 2>/dev/null || echo "0")
|
|
145
|
+
echo " $level: $count"
|
|
146
|
+
done
|
|
147
|
+
echo ""
|
|
148
|
+
echo "Total Lines: $(wc -l < "$LOG_FILE" | tr -d ' ')"
|
|
149
|
+
echo "File Size: $(du -h "$LOG_FILE" | cut -f1)"
|
|
150
|
+
else
|
|
151
|
+
echo "No log file found"
|
|
152
|
+
fi
|
|
153
|
+
|
|
154
|
+
echo ""
|
|
155
|
+
local event_log="$LOG_DIR/events.jsonl"
|
|
156
|
+
if [ -f "$event_log" ]; then
|
|
157
|
+
echo "Events:"
|
|
158
|
+
echo " Total: $(wc -l < "$event_log" | tr -d ' ')"
|
|
159
|
+
if command -v jq &> /dev/null; then
|
|
160
|
+
echo " By Type:"
|
|
161
|
+
jq -r '.type' "$event_log" 2>/dev/null | sort | uniq -c | sort -rn | head -5 | while read count type; do
|
|
162
|
+
echo " $type: $count"
|
|
163
|
+
done
|
|
164
|
+
fi
|
|
165
|
+
fi
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
# 로그 정리 (오래된 로그 삭제)
|
|
169
|
+
cleanup_logs() {
|
|
170
|
+
local days="${1:-30}"
|
|
171
|
+
|
|
172
|
+
echo "Cleaning logs older than $days days..."
|
|
173
|
+
|
|
174
|
+
# 메인 로그 로테이션
|
|
175
|
+
if [ -f "$LOG_FILE" ]; then
|
|
176
|
+
local cutoff_date=$(date -v-${days}d +"%Y-%m-%d" 2>/dev/null || date -d "$days days ago" +"%Y-%m-%d" 2>/dev/null)
|
|
177
|
+
if [ -n "$cutoff_date" ]; then
|
|
178
|
+
# 백업 후 정리
|
|
179
|
+
cp "$LOG_FILE" "$LOG_FILE.bak"
|
|
180
|
+
grep -E "^\[${cutoff_date}|^\[$(date +%Y-)" "$LOG_FILE.bak" > "$LOG_FILE" 2>/dev/null || true
|
|
181
|
+
rm -f "$LOG_FILE.bak"
|
|
182
|
+
echo "Main log cleaned"
|
|
183
|
+
fi
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# 이벤트 로그 정리
|
|
187
|
+
local event_log="$LOG_DIR/events.jsonl"
|
|
188
|
+
if [ -f "$event_log" ] && command -v jq &> /dev/null; then
|
|
189
|
+
local cutoff_ts=$(date -v-${days}d -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date -d "$days days ago" -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null)
|
|
190
|
+
if [ -n "$cutoff_ts" ]; then
|
|
191
|
+
cp "$event_log" "$event_log.bak"
|
|
192
|
+
jq -c --arg ts "$cutoff_ts" 'select(.timestamp >= $ts)' "$event_log.bak" > "$event_log" 2>/dev/null || true
|
|
193
|
+
rm -f "$event_log.bak"
|
|
194
|
+
echo "Event log cleaned"
|
|
195
|
+
fi
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
echo "Done"
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
# 로그 파일 초기화
|
|
202
|
+
clear_logs() {
|
|
203
|
+
rm -f "$LOG_FILE"
|
|
204
|
+
rm -f "$LOG_DIR/events.jsonl"
|
|
205
|
+
echo "Logs cleared"
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# CLI 인터페이스
|
|
209
|
+
case "$1" in
|
|
210
|
+
debug)
|
|
211
|
+
log_debug "$2" "$3"
|
|
212
|
+
;;
|
|
213
|
+
info)
|
|
214
|
+
log_info "$2" "$3"
|
|
215
|
+
;;
|
|
216
|
+
warn)
|
|
217
|
+
log_warn "$2" "$3"
|
|
218
|
+
;;
|
|
219
|
+
error)
|
|
220
|
+
log_error "$2" "$3"
|
|
221
|
+
;;
|
|
222
|
+
event)
|
|
223
|
+
log_event "$2" "$3"
|
|
224
|
+
;;
|
|
225
|
+
view)
|
|
226
|
+
view_logs "$2" "$3"
|
|
227
|
+
;;
|
|
228
|
+
search)
|
|
229
|
+
search_logs "$2" "$3"
|
|
230
|
+
;;
|
|
231
|
+
events)
|
|
232
|
+
view_events "$2" "$3"
|
|
233
|
+
;;
|
|
234
|
+
stats)
|
|
235
|
+
log_stats
|
|
236
|
+
;;
|
|
237
|
+
cleanup)
|
|
238
|
+
cleanup_logs "$2"
|
|
239
|
+
;;
|
|
240
|
+
clear)
|
|
241
|
+
clear_logs
|
|
242
|
+
;;
|
|
243
|
+
*)
|
|
244
|
+
echo "Usage: $0 {debug|info|warn|error|event|view|search|events|stats|cleanup|clear}"
|
|
245
|
+
echo ""
|
|
246
|
+
echo "Commands:"
|
|
247
|
+
echo " debug <msg> [ctx] - Log debug message"
|
|
248
|
+
echo " info <msg> [ctx] - Log info message"
|
|
249
|
+
echo " warn <msg> [ctx] - Log warning message"
|
|
250
|
+
echo " error <msg> [ctx] - Log error message"
|
|
251
|
+
echo " event <type> <json> - Log structured event"
|
|
252
|
+
echo " view [lines] [level] - View recent logs"
|
|
253
|
+
echo " search <pattern> - Search logs"
|
|
254
|
+
echo " events [count] [type] - View events"
|
|
255
|
+
echo " stats - Show log statistics"
|
|
256
|
+
echo " cleanup [days] - Clean old logs"
|
|
257
|
+
echo " clear - Clear all logs"
|
|
258
|
+
echo ""
|
|
259
|
+
echo "Environment:"
|
|
260
|
+
echo " SCOUT_LOG_LEVEL - Set log level (DEBUG, INFO, WARN, ERROR)"
|
|
261
|
+
echo " SCOUT_DEBUG - Enable debug output to stdout"
|
|
262
|
+
;;
|
|
263
|
+
esac
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Plugin Scout - 플러그인 매니저
|
|
3
|
+
# 플러그인 설치/제거/업데이트 자동화
|
|
4
|
+
|
|
5
|
+
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(dirname "$0")/..}"
|
|
6
|
+
DATA_DIR="$PLUGIN_ROOT/data"
|
|
7
|
+
HISTORY_FILE="$DATA_DIR/history.json"
|
|
8
|
+
USAGE_FILE="$DATA_DIR/usage.json"
|
|
9
|
+
|
|
10
|
+
CLAUDE_SETTINGS="$HOME/.claude/settings.json"
|
|
11
|
+
CLAUDE_PLUGINS_DIR="$HOME/.claude/plugins"
|
|
12
|
+
|
|
13
|
+
# 플러그인 설치
|
|
14
|
+
install_plugin() {
|
|
15
|
+
local plugin_spec="$1" # format: plugin-name@marketplace or plugin-name
|
|
16
|
+
local source="${2:-recommendation}"
|
|
17
|
+
|
|
18
|
+
# 플러그인명과 마켓플레이스 분리
|
|
19
|
+
local plugin_name="${plugin_spec%@*}"
|
|
20
|
+
local marketplace="${plugin_spec#*@}"
|
|
21
|
+
|
|
22
|
+
if [ "$marketplace" = "$plugin_spec" ]; then
|
|
23
|
+
marketplace=""
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
echo "Installing plugin: $plugin_name"
|
|
27
|
+
|
|
28
|
+
# settings.json 백업
|
|
29
|
+
if [ -f "$CLAUDE_SETTINGS" ]; then
|
|
30
|
+
cp "$CLAUDE_SETTINGS" "$CLAUDE_SETTINGS.bak"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# enabledPlugins에 추가
|
|
34
|
+
if command -v jq &> /dev/null && [ -f "$CLAUDE_SETTINGS" ]; then
|
|
35
|
+
local plugin_key="$plugin_name"
|
|
36
|
+
if [ -n "$marketplace" ]; then
|
|
37
|
+
plugin_key="${plugin_name}@${marketplace}"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
jq --arg key "$plugin_key" '.enabledPlugins[$key] = true' "$CLAUDE_SETTINGS" > "$CLAUDE_SETTINGS.tmp" && mv "$CLAUDE_SETTINGS.tmp" "$CLAUDE_SETTINGS"
|
|
41
|
+
|
|
42
|
+
# history.json에 설치 기록
|
|
43
|
+
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
44
|
+
local date_only=$(date +"%Y-%m-%d")
|
|
45
|
+
|
|
46
|
+
if [ -f "$HISTORY_FILE" ]; then
|
|
47
|
+
jq --arg plugin "$plugin_name" \
|
|
48
|
+
--arg date "$date_only" \
|
|
49
|
+
--arg source "$source" \
|
|
50
|
+
--arg ts "$timestamp" \
|
|
51
|
+
'
|
|
52
|
+
.lastUpdated = $ts |
|
|
53
|
+
.installed[$plugin] = {
|
|
54
|
+
date: $date,
|
|
55
|
+
source: $source,
|
|
56
|
+
installedAt: $ts
|
|
57
|
+
}
|
|
58
|
+
' "$HISTORY_FILE" > "$HISTORY_FILE.tmp" && mv "$HISTORY_FILE.tmp" "$HISTORY_FILE"
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# usage.json에 추가
|
|
62
|
+
if [ -f "$USAGE_FILE" ]; then
|
|
63
|
+
jq --arg plugin "$plugin_name" \
|
|
64
|
+
--arg date "$date_only" \
|
|
65
|
+
--arg ts "$timestamp" \
|
|
66
|
+
'
|
|
67
|
+
.lastUpdated = $ts |
|
|
68
|
+
.plugins[$plugin] = (.plugins[$plugin] // {
|
|
69
|
+
installed: $date,
|
|
70
|
+
usageCount: 0,
|
|
71
|
+
lastUsed: null
|
|
72
|
+
})
|
|
73
|
+
' "$USAGE_FILE" > "$USAGE_FILE.tmp" && mv "$USAGE_FILE.tmp" "$USAGE_FILE"
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
echo "Installed: $plugin_key"
|
|
77
|
+
echo "Restart Claude Code to activate the plugin."
|
|
78
|
+
return 0
|
|
79
|
+
else
|
|
80
|
+
echo "Error: jq required or settings.json not found"
|
|
81
|
+
return 1
|
|
82
|
+
fi
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# 플러그인 제거
|
|
86
|
+
uninstall_plugin() {
|
|
87
|
+
local plugin_name="$1"
|
|
88
|
+
|
|
89
|
+
echo "Uninstalling plugin: $plugin_name"
|
|
90
|
+
|
|
91
|
+
if command -v jq &> /dev/null && [ -f "$CLAUDE_SETTINGS" ]; then
|
|
92
|
+
# settings.json 백업
|
|
93
|
+
cp "$CLAUDE_SETTINGS" "$CLAUDE_SETTINGS.bak"
|
|
94
|
+
|
|
95
|
+
# enabledPlugins에서 제거 (정확한 키 또는 @가 포함된 키)
|
|
96
|
+
jq --arg plugin "$plugin_name" '
|
|
97
|
+
.enabledPlugins |= with_entries(
|
|
98
|
+
select(
|
|
99
|
+
.key != $plugin and
|
|
100
|
+
(.key | startswith($plugin + "@") | not)
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
' "$CLAUDE_SETTINGS" > "$CLAUDE_SETTINGS.tmp" && mv "$CLAUDE_SETTINGS.tmp" "$CLAUDE_SETTINGS"
|
|
104
|
+
|
|
105
|
+
# history.json에서 installed 제거
|
|
106
|
+
if [ -f "$HISTORY_FILE" ]; then
|
|
107
|
+
local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
108
|
+
jq --arg plugin "$plugin_name" --arg ts "$timestamp" '
|
|
109
|
+
.lastUpdated = $ts |
|
|
110
|
+
del(.installed[$plugin])
|
|
111
|
+
' "$HISTORY_FILE" > "$HISTORY_FILE.tmp" && mv "$HISTORY_FILE.tmp" "$HISTORY_FILE"
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
echo "Uninstalled: $plugin_name"
|
|
115
|
+
echo "Restart Claude Code to complete removal."
|
|
116
|
+
return 0
|
|
117
|
+
else
|
|
118
|
+
echo "Error: jq required or settings.json not found"
|
|
119
|
+
return 1
|
|
120
|
+
fi
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# 설치된 플러그인 목록
|
|
124
|
+
list_installed() {
|
|
125
|
+
if command -v jq &> /dev/null && [ -f "$CLAUDE_SETTINGS" ]; then
|
|
126
|
+
echo "Installed Plugins:"
|
|
127
|
+
jq -r '.enabledPlugins | to_entries | .[] | select(.value == true) | " - \(.key)"' "$CLAUDE_SETTINGS"
|
|
128
|
+
else
|
|
129
|
+
echo "Error: jq required or settings.json not found"
|
|
130
|
+
fi
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# 플러그인 상태 확인
|
|
134
|
+
check_status() {
|
|
135
|
+
local plugin_name="$1"
|
|
136
|
+
|
|
137
|
+
if command -v jq &> /dev/null && [ -f "$CLAUDE_SETTINGS" ]; then
|
|
138
|
+
local enabled=$(jq -r --arg plugin "$plugin_name" '
|
|
139
|
+
.enabledPlugins | to_entries | .[] |
|
|
140
|
+
select(.key == $plugin or (.key | startswith($plugin + "@"))) |
|
|
141
|
+
.value
|
|
142
|
+
' "$CLAUDE_SETTINGS")
|
|
143
|
+
|
|
144
|
+
if [ "$enabled" = "true" ]; then
|
|
145
|
+
echo "enabled"
|
|
146
|
+
else
|
|
147
|
+
echo "disabled"
|
|
148
|
+
fi
|
|
149
|
+
else
|
|
150
|
+
echo "unknown"
|
|
151
|
+
fi
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# 플러그인 활성화/비활성화
|
|
155
|
+
toggle_plugin() {
|
|
156
|
+
local plugin_name="$1"
|
|
157
|
+
local action="$2" # enable or disable
|
|
158
|
+
|
|
159
|
+
if command -v jq &> /dev/null && [ -f "$CLAUDE_SETTINGS" ]; then
|
|
160
|
+
local value="true"
|
|
161
|
+
[ "$action" = "disable" ] && value="false"
|
|
162
|
+
|
|
163
|
+
jq --arg plugin "$plugin_name" --argjson val "$value" '
|
|
164
|
+
.enabledPlugins |= with_entries(
|
|
165
|
+
if .key == $plugin or (.key | startswith($plugin + "@"))
|
|
166
|
+
then .value = $val
|
|
167
|
+
else .
|
|
168
|
+
end
|
|
169
|
+
)
|
|
170
|
+
' "$CLAUDE_SETTINGS" > "$CLAUDE_SETTINGS.tmp" && mv "$CLAUDE_SETTINGS.tmp" "$CLAUDE_SETTINGS"
|
|
171
|
+
|
|
172
|
+
echo "Plugin $plugin_name: $action"
|
|
173
|
+
else
|
|
174
|
+
echo "Error: jq required or settings.json not found"
|
|
175
|
+
fi
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# 설치 명령 생성 (dry-run)
|
|
179
|
+
generate_install_command() {
|
|
180
|
+
local plugin_name="$1"
|
|
181
|
+
local marketplace="$2"
|
|
182
|
+
|
|
183
|
+
if [ -n "$marketplace" ]; then
|
|
184
|
+
echo "/plugin install ${plugin_name}@${marketplace}"
|
|
185
|
+
else
|
|
186
|
+
echo "/plugin install ${plugin_name}"
|
|
187
|
+
fi
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# 복원 (백업에서)
|
|
191
|
+
restore_backup() {
|
|
192
|
+
if [ -f "$CLAUDE_SETTINGS.bak" ]; then
|
|
193
|
+
cp "$CLAUDE_SETTINGS.bak" "$CLAUDE_SETTINGS"
|
|
194
|
+
echo "Restored from backup"
|
|
195
|
+
else
|
|
196
|
+
echo "No backup found"
|
|
197
|
+
fi
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# CLI 인터페이스
|
|
201
|
+
case "$1" in
|
|
202
|
+
install)
|
|
203
|
+
install_plugin "$2" "$3"
|
|
204
|
+
;;
|
|
205
|
+
uninstall|remove)
|
|
206
|
+
uninstall_plugin "$2"
|
|
207
|
+
;;
|
|
208
|
+
list)
|
|
209
|
+
list_installed
|
|
210
|
+
;;
|
|
211
|
+
status)
|
|
212
|
+
check_status "$2"
|
|
213
|
+
;;
|
|
214
|
+
enable)
|
|
215
|
+
toggle_plugin "$2" "enable"
|
|
216
|
+
;;
|
|
217
|
+
disable)
|
|
218
|
+
toggle_plugin "$2" "disable"
|
|
219
|
+
;;
|
|
220
|
+
command)
|
|
221
|
+
generate_install_command "$2" "$3"
|
|
222
|
+
;;
|
|
223
|
+
restore)
|
|
224
|
+
restore_backup
|
|
225
|
+
;;
|
|
226
|
+
*)
|
|
227
|
+
echo "Usage: $0 {install|uninstall|list|status|enable|disable|command|restore}"
|
|
228
|
+
echo ""
|
|
229
|
+
echo "Commands:"
|
|
230
|
+
echo " install <plugin[@marketplace]> [source] - Install a plugin"
|
|
231
|
+
echo " uninstall <plugin> - Uninstall a plugin"
|
|
232
|
+
echo " list - List installed plugins"
|
|
233
|
+
echo " status <plugin> - Check plugin status"
|
|
234
|
+
echo " enable <plugin> - Enable a plugin"
|
|
235
|
+
echo " disable <plugin> - Disable a plugin"
|
|
236
|
+
echo " command <plugin> [marketplace] - Generate install command"
|
|
237
|
+
echo " restore - Restore from backup"
|
|
238
|
+
;;
|
|
239
|
+
esac
|