muno-claude-plugin 1.8.0 → 1.10.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.
@@ -4,7 +4,7 @@ description: |
4
4
  Swagger/OpenAPI 문서를 읽어서 개발자가 읽기 쉬운 Markdown API 명세로 변환합니다.
5
5
  "API 문서 만들어줘", "Swagger 문서 변환", "API 명세 생성", "OpenAPI 문서" 등의 요청에 사용합니다.
6
6
  서버가 제공하는 Swagger JSON을 자동으로 파싱하여 체계적인 문서를 생성합니다.
7
- allowed-tools: Read, Write, WebFetch, Grep, Glob
7
+ allowed-tools: Read, Write, WebFetch, Grep, Glob, Bash
8
8
  ---
9
9
 
10
10
  # Swagger Docs Generator
@@ -571,6 +571,76 @@ curl -X POST "http://localhost:8080/api/users" \
571
571
 
572
572
  ---
573
573
 
574
+ ## 실행 방법
575
+
576
+ ### 스크립트 활용
577
+
578
+ 이 스킬은 파이썬 스크립트를 사용하여 Swagger 문서를 변환합니다.
579
+
580
+ #### 1. 로컬 IP 감지
581
+
582
+ ```bash
583
+ # 스크립트 위치: scripts/get-local-ip.py
584
+ SKILL_DIR=$(pwd)/.claude/skills/swagger-docs-generator
585
+
586
+ # 로컬 IP 감지
587
+ python3 $SKILL_DIR/scripts/get-local-ip.py
588
+
589
+ # 출력 예시:
590
+ # [
591
+ # {"ip": "192.168.0.100", "interface": "en0", "type": "WiFi"},
592
+ # {"ip": "127.0.0.1", "interface": "lo0", "type": "Loopback"}
593
+ # ]
594
+ ```
595
+
596
+ #### 2. Swagger → Markdown 변환
597
+
598
+ ```bash
599
+ # 스크립트 위치: scripts/swagger-to-markdown.py
600
+ SKILL_DIR=$(pwd)/.claude/skills/swagger-docs-generator
601
+
602
+ # 기본 사용
603
+ python3 $SKILL_DIR/scripts/swagger-to-markdown.py http://localhost:8080/v3/api-docs
604
+
605
+ # 파일로 저장
606
+ python3 $SKILL_DIR/scripts/swagger-to-markdown.py \
607
+ http://localhost:8080/v3/api-docs \
608
+ documents/api/user-service-api-docs.md
609
+
610
+ # 로컬 IP 자동 감지 + 변환 (통합 실행)
611
+ LOCAL_IP=$(python3 $SKILL_DIR/scripts/get-local-ip.py | jq -r '.[0].ip')
612
+ python3 $SKILL_DIR/scripts/swagger-to-markdown.py \
613
+ http://$LOCAL_IP:8080/v3/api-docs \
614
+ documents/api/api-docs.md
615
+ ```
616
+
617
+ #### 3. 스킬 워크플로우
618
+
619
+ 사용자가 `/swagger-docs-generator`를 호출하면:
620
+
621
+ 1. **로컬 IP 감지**
622
+ ```bash
623
+ python3 scripts/get-local-ip.py
624
+ ```
625
+
626
+ 2. **사용자 확인** (여러 IP가 감지되면)
627
+ - 감지된 IP 목록 표시
628
+ - 사용자 선택 받기
629
+
630
+ 3. **Swagger 문서 변환**
631
+ ```bash
632
+ python3 scripts/swagger-to-markdown.py \
633
+ http://{selected_ip}:{port}/v3/api-docs \
634
+ documents/api/{service-name}-api-docs.md
635
+ ```
636
+
637
+ 4. **결과 전달**
638
+ - 생성된 파일 경로
639
+ - API 엔드포인트 요약
640
+ - Swagger UI 링크
641
+
642
+ ---
643
+
574
644
  ## 실행 예시
575
645
 
576
646
  ### Input
@@ -584,12 +654,10 @@ http://localhost:8080/v3/api-docs 의 API 문서를 생성해주세요.
584
654
 
585
655
  ### Process
586
656
 
587
- 1. WebFetch로 `http://localhost:8080/v3/api-docs` 요청
588
- 2. JSON 파싱
589
- 3. 엔드포인트 추출태그별 그룹핑
590
- 4. 스키마 정의 추출
591
- 5. Markdown 문서 생성
592
- 6. `documents/api/user-service-api-docs.md` 저장
657
+ 1. `python3 scripts/swagger-to-markdown.py` 실행
658
+ 2. Swagger JSON 가져오기
659
+ 3. JSON 파싱Markdown 생성
660
+ 4. `documents/api/user-service-api-docs.md` 저장
593
661
 
594
662
  ### Output
595
663
 
@@ -634,10 +702,10 @@ http://localhost:8080/v3/api-docs 의 API 문서를 생성해주세요.
634
702
 
635
703
  ---
636
704
 
637
- ## 참고 템플릿
705
+ ## 참고 문서
638
706
 
639
- 상세한 Markdown 템플릿은 다음 파일 참고:
640
- - `reference/api-docs-template.md`
707
+ 상세한 Markdown 템플릿 예시:
708
+ - [reference/api-docs-template.md](reference/api-docs-template.md) - API 문서 템플릿 및 예시
641
709
 
642
710
  ---
643
711
 
@@ -0,0 +1,329 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 로컬 IP 주소를 자동으로 감지하는 스크립트
4
+
5
+ Usage:
6
+ python get-local-ip.py
7
+
8
+ Output:
9
+ JSON 형식으로 감지된 IP 주소 목록 출력
10
+ [
11
+ {"ip": "192.168.0.100", "interface": "en0", "type": "WiFi"},
12
+ {"ip": "172.16.0.50", "interface": "en1", "type": "Ethernet"},
13
+ {"ip": "127.0.0.1", "interface": "lo0", "type": "Loopback"}
14
+ ]
15
+ """
16
+
17
+ import socket
18
+ import json
19
+ import platform
20
+ import subprocess
21
+ from typing import List, Dict
22
+
23
+ def get_local_ips() -> List[Dict[str, str]]:
24
+ """모든 네트워크 인터페이스의 IP 주소를 감지"""
25
+ ips = []
26
+
27
+ # Method 1: socket을 이용한 기본 IP 감지 (가장 신뢰할 수 있는 방법)
28
+ try:
29
+ # 외부 연결을 시뮬레이션하여 실제 사용되는 IP 얻기
30
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
31
+ s.settimeout(0.1)
32
+ try:
33
+ # Google DNS에 연결 (실제로 패킷을 보내지 않음)
34
+ s.connect(('8.8.8.8', 80))
35
+ primary_ip = s.getsockname()[0]
36
+ ips.append({
37
+ 'ip': primary_ip,
38
+ 'interface': 'primary',
39
+ 'type': 'Primary',
40
+ 'priority': 1
41
+ })
42
+ except Exception:
43
+ pass
44
+ finally:
45
+ s.close()
46
+ except Exception:
47
+ pass
48
+
49
+ # Method 2: 플랫폼별 네트워크 인터페이스 조회
50
+ system = platform.system()
51
+
52
+ if system == 'Darwin': # macOS
53
+ ips.extend(get_macos_ips())
54
+ elif system == 'Linux':
55
+ ips.extend(get_linux_ips())
56
+ elif system == 'Windows':
57
+ ips.extend(get_windows_ips())
58
+
59
+ # 중복 제거 (IP 기준)
60
+ seen_ips = set()
61
+ unique_ips = []
62
+ for ip_info in ips:
63
+ if ip_info['ip'] not in seen_ips:
64
+ seen_ips.add(ip_info['ip'])
65
+ unique_ips.append(ip_info)
66
+
67
+ # 우선순위 정렬
68
+ unique_ips.sort(key=lambda x: (
69
+ x.get('priority', 99), # 우선순위
70
+ 0 if x['ip'].startswith('192.168.') else 1, # 사설 IP 우선
71
+ x['ip'] # IP 주소
72
+ ))
73
+
74
+ return unique_ips
75
+
76
+ def get_macos_ips() -> List[Dict[str, str]]:
77
+ """macOS에서 IP 주소 감지"""
78
+ ips = []
79
+
80
+ try:
81
+ # ifconfig 명령어 실행
82
+ result = subprocess.run(
83
+ ['ifconfig'],
84
+ capture_output=True,
85
+ text=True,
86
+ timeout=5
87
+ )
88
+
89
+ if result.returncode == 0:
90
+ current_interface = None
91
+ interface_type = 'Unknown'
92
+
93
+ for line in result.stdout.split('\n'):
94
+ line = line.strip()
95
+
96
+ # 인터페이스 이름 감지
97
+ if line and not line.startswith(' ') and ':' in line:
98
+ current_interface = line.split(':')[0]
99
+
100
+ # 인터페이스 타입 판단
101
+ if current_interface.startswith('en0'):
102
+ interface_type = 'WiFi'
103
+ elif current_interface.startswith('en1'):
104
+ interface_type = 'Ethernet'
105
+ elif current_interface.startswith('lo'):
106
+ interface_type = 'Loopback'
107
+ else:
108
+ interface_type = 'Other'
109
+
110
+ # inet 주소 추출
111
+ if 'inet ' in line and current_interface:
112
+ parts = line.split()
113
+ if len(parts) >= 2:
114
+ ip = parts[1]
115
+
116
+ # 우선순위 결정
117
+ priority = 10
118
+ if interface_type == 'WiFi':
119
+ priority = 2
120
+ elif interface_type == 'Ethernet':
121
+ priority = 3
122
+ elif interface_type == 'Loopback':
123
+ priority = 99
124
+
125
+ ips.append({
126
+ 'ip': ip,
127
+ 'interface': current_interface,
128
+ 'type': interface_type,
129
+ 'priority': priority
130
+ })
131
+
132
+ # ipconfig 명령어로 추가 시도
133
+ for interface in ['en0', 'en1', 'en2']:
134
+ try:
135
+ result = subprocess.run(
136
+ ['ipconfig', 'getifaddr', interface],
137
+ capture_output=True,
138
+ text=True,
139
+ timeout=2
140
+ )
141
+ if result.returncode == 0 and result.stdout.strip():
142
+ ip = result.stdout.strip()
143
+ interface_type = 'WiFi' if interface == 'en0' else 'Ethernet'
144
+ priority = 2 if interface == 'en0' else 3
145
+
146
+ ips.append({
147
+ 'ip': ip,
148
+ 'interface': interface,
149
+ 'type': interface_type,
150
+ 'priority': priority
151
+ })
152
+ except Exception:
153
+ continue
154
+
155
+ except Exception as e:
156
+ print(f"macOS IP detection error: {e}", file=sys.stderr)
157
+
158
+ return ips
159
+
160
+ def get_linux_ips() -> List[Dict[str, str]]:
161
+ """Linux에서 IP 주소 감지"""
162
+ ips = []
163
+
164
+ try:
165
+ # hostname -I 명령어
166
+ result = subprocess.run(
167
+ ['hostname', '-I'],
168
+ capture_output=True,
169
+ text=True,
170
+ timeout=5
171
+ )
172
+
173
+ if result.returncode == 0:
174
+ for idx, ip in enumerate(result.stdout.strip().split()):
175
+ ips.append({
176
+ 'ip': ip,
177
+ 'interface': f'auto-{idx}',
178
+ 'type': 'Auto-detected',
179
+ 'priority': 5 + idx
180
+ })
181
+
182
+ # ip addr 명령어
183
+ result = subprocess.run(
184
+ ['ip', 'addr'],
185
+ capture_output=True,
186
+ text=True,
187
+ timeout=5
188
+ )
189
+
190
+ if result.returncode == 0:
191
+ current_interface = None
192
+ interface_type = 'Unknown'
193
+
194
+ for line in result.stdout.split('\n'):
195
+ line = line.strip()
196
+
197
+ # 인터페이스 감지
198
+ if line and line[0].isdigit() and ':' in line:
199
+ parts = line.split(':')
200
+ if len(parts) >= 2:
201
+ current_interface = parts[1].strip()
202
+
203
+ if 'wlan' in current_interface or 'wlp' in current_interface:
204
+ interface_type = 'WiFi'
205
+ elif 'eth' in current_interface or 'enp' in current_interface:
206
+ interface_type = 'Ethernet'
207
+ elif 'lo' in current_interface:
208
+ interface_type = 'Loopback'
209
+ else:
210
+ interface_type = 'Other'
211
+
212
+ # IP 주소 추출
213
+ if 'inet ' in line and current_interface:
214
+ parts = line.split()
215
+ if len(parts) >= 2:
216
+ ip_with_mask = parts[1]
217
+ ip = ip_with_mask.split('/')[0]
218
+
219
+ priority = 10
220
+ if interface_type == 'WiFi':
221
+ priority = 2
222
+ elif interface_type == 'Ethernet':
223
+ priority = 3
224
+ elif interface_type == 'Loopback':
225
+ priority = 99
226
+
227
+ ips.append({
228
+ 'ip': ip,
229
+ 'interface': current_interface,
230
+ 'type': interface_type,
231
+ 'priority': priority
232
+ })
233
+
234
+ except Exception as e:
235
+ print(f"Linux IP detection error: {e}", file=sys.stderr)
236
+
237
+ return ips
238
+
239
+ def get_windows_ips() -> List[Dict[str, str]]:
240
+ """Windows에서 IP 주소 감지"""
241
+ ips = []
242
+
243
+ try:
244
+ # ipconfig 명령어
245
+ result = subprocess.run(
246
+ ['ipconfig'],
247
+ capture_output=True,
248
+ text=True,
249
+ timeout=5,
250
+ encoding='cp949' # Windows 인코딩
251
+ )
252
+
253
+ if result.returncode == 0:
254
+ current_adapter = None
255
+ adapter_type = 'Unknown'
256
+
257
+ for line in result.stdout.split('\n'):
258
+ line = line.strip()
259
+
260
+ # 어댑터 이름 감지
261
+ if 'adapter' in line.lower() or '어댑터' in line:
262
+ current_adapter = line.split(':')[0].strip()
263
+
264
+ if 'wireless' in line.lower() or '무선' in line:
265
+ adapter_type = 'WiFi'
266
+ elif 'ethernet' in line.lower() or '이더넷' in line:
267
+ adapter_type = 'Ethernet'
268
+ else:
269
+ adapter_type = 'Other'
270
+
271
+ # IPv4 주소 추출
272
+ if ('IPv4' in line or 'IPv4 주소' in line) and ':' in line:
273
+ ip = line.split(':')[-1].strip()
274
+ # (기본 설정) 같은 문구 제거
275
+ ip = ip.replace('(기본 설정)', '').strip()
276
+
277
+ priority = 10
278
+ if adapter_type == 'WiFi':
279
+ priority = 2
280
+ elif adapter_type == 'Ethernet':
281
+ priority = 3
282
+
283
+ ips.append({
284
+ 'ip': ip,
285
+ 'interface': current_adapter or 'unknown',
286
+ 'type': adapter_type,
287
+ 'priority': priority
288
+ })
289
+
290
+ except Exception as e:
291
+ print(f"Windows IP detection error: {e}", file=sys.stderr)
292
+
293
+ return ips
294
+
295
+ def main():
296
+ """메인 함수"""
297
+ try:
298
+ ips = get_local_ips()
299
+
300
+ if not ips:
301
+ # 기본값으로 localhost 추가
302
+ ips.append({
303
+ 'ip': '127.0.0.1',
304
+ 'interface': 'lo0',
305
+ 'type': 'Loopback',
306
+ 'priority': 99
307
+ })
308
+
309
+ # JSON 출력 (priority 제외)
310
+ output = []
311
+ for ip_info in ips:
312
+ output.append({
313
+ 'ip': ip_info['ip'],
314
+ 'interface': ip_info['interface'],
315
+ 'type': ip_info['type']
316
+ })
317
+
318
+ print(json.dumps(output, indent=2, ensure_ascii=False))
319
+
320
+ except Exception as e:
321
+ import sys
322
+ print(json.dumps({
323
+ 'error': str(e),
324
+ 'ips': [{'ip': '127.0.0.1', 'interface': 'lo0', 'type': 'Loopback'}]
325
+ }, indent=2), file=sys.stderr)
326
+ sys.exit(1)
327
+
328
+ if __name__ == '__main__':
329
+ main()