How this skill is triggered — by the user, by Claude, or both
Slash command
/creative:sort-filesThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
지정 폴더(기본: `~/Downloads`)의 파일을 카테고리별 하위 폴더로 분류하고, MD5 기준 중복 파일을 삭제한 뒤 서브에이전트를 병렬로 실행해 파일을 이동합니다.
지정 폴더(기본: ~/Downloads)의 파일을 카테고리별 하위 폴더로 분류하고, MD5 기준 중복 파일을 삭제한 뒤 서브에이전트를 병렬로 실행해 파일을 이동합니다.
$ARGUMENTS 가 비어 있으면 ~/Downloads를 대상 폴더로 사용합니다.
$ARGUMENTS가 있으면 해당 경로, 없으면 ~/Downloads를 TARGET으로 설정합니다.
아래 Python 스크립트를 Bash로 실행합니다.
import os, hashlib, json, re
TARGET = "$ARGUMENTS" if "$ARGUMENTS".strip() else os.path.expanduser("~/Downloads")
CATEGORIES = ["이미지", "동영상", "설치파일", "폰트", "개인문서", "업무문서", "_Office임시"]
for c in CATEGORIES:
os.makedirs(os.path.join(TARGET, c), exist_ok=True)
all_files = [
n for n in os.listdir(TARGET)
if not n.startswith('.') and n not in CATEGORIES
]
def md5(path):
try:
h = hashlib.md5()
with open(path, 'rb') as f:
for chunk in iter(lambda: f.read(65536), b''):
h.update(chunk)
return h.hexdigest()
except:
return None
dup_pattern = re.compile(r'^(.+) \((\d+)\)(\.[^.]+)$')
duplicates = []
for name in all_files:
m = dup_pattern.match(name)
if m:
base = m.group(1) + m.group(3)
fp = os.path.join(TARGET, name)
fb = os.path.join(TARGET, base)
if os.path.isfile(fb) and os.path.isfile(fp) and md5(fp) == md5(fb):
duplicates.append(name)
IMAGE_EXT = {'.png','.jpg','.jpeg','.PNG','.JPEG','.JPG','.gif','.webp','.heic'}
VIDEO_EXT = {'.mp4','.mov','.mp3','.m4a','.avi','.mkv','.webm'}
FONT_EXT = {'.otf','.ttf','.woff','.woff2'}
INSTALL_EXT= {'.dmg','.zip','.pkg','.xip','.exe','.msi','.deb','.rpm','.pem','.log','.ozr'}
PERSONAL_KW = [
'혼인증명서','연봉산정표','독감예방접종','출장비신청','연말정산','경력입사자 OT',
'이사짐','회식일정','리조트','카카오페이','금소법','approve2022','ESP 안내',
'HR Lounge','국세청 간소화','스마트 연말정산','시뮬레이션 메뉴얼','가족사항 변경',
'협력병원','SubevalEvGoalFram','근무시간입력시스템','재택근무 DRM',
'ViewTManual','View-T Manual','PtMrgRltCertPc','사계절휴양소','휴양소 신청',
'휴양소(테마파크','RcEngMgr','자주묻는 질문','교육신청방법',
]
def categorize(name):
if name.startswith('~$'):
return '_Office임시'
_, ext = os.path.splitext(name)
if ext in IMAGE_EXT: return '이미지'
if ext in VIDEO_EXT: return '동영상'
if ext in FONT_EXT: return '폰트'
if ext in INSTALL_EXT: return '설치파일'
if not ext: return '설치파일'
for kw in PERSONAL_KW:
if kw in name: return '개인문서'
return '업무문서'
dup_set = set(duplicates)
moves = [(n, categorize(n)) for n in all_files if n not in dup_set]
from collections import Counter
stats = Counter(cat for _, cat in moves)
print(f"대상 폴더: {TARGET}")
print(f"전체 파일: {len(all_files)}개")
print(f"삭제 대상(중복): {len(duplicates)}개")
print(f"이동 대상: {len(moves)}개")
print("카테고리별:")
for cat in CATEGORIES:
print(f" {cat}/: {stats.get(cat,0)}개")
BATCH_SIZE = 50
batches = [moves[i:i+BATCH_SIZE] for i in range(0, len(moves), BATCH_SIZE)]
data = {"target": TARGET, "duplicates": duplicates, "batches": batches}
with open('/tmp/sort_files_plan.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print(f"배치 수: {len(batches)}개 (/tmp/sort_files_plan.json 저장)")
스크립트 출력(통계)을 보여주고 계속 진행할지 확인합니다.
import json, os
with open('/tmp/sort_files_plan.json', encoding='utf-8') as f:
data = json.load(f)
deleted = 0
for name in data['duplicates']:
path = os.path.join(data['target'], name)
if os.path.exists(path):
os.remove(path)
deleted += 1
print(f"중복 {deleted}개 삭제 완료")
import json
with open('/tmp/sort_files_plan.json', encoding='utf-8') as f:
data = json.load(f)
for i, batch in enumerate(data['batches']):
with open(f'/tmp/sort_batch_{i+1}.json', 'w', encoding='utf-8') as f:
json.dump({"target": data['target'], "batch": batch}, f, ensure_ascii=False)
print(f"{len(data['batches'])}개 배치 파일 저장 완료")
배치 수만큼 Agent를 한 번에 실행합니다 (run_in_background=true).
각 에이전트 프롬프트:
/tmp/sort_batch_{N}.json 파일을 읽어 파일을 이동하세요.
Python 코드:
import json, os, shutil
with open('/tmp/sort_batch_{N}.json', encoding='utf-8') as f:
d = json.load(f)
target = d['target']
moved, errors = 0, []
for name, cat in d['batch']:
src = os.path.join(target, name)
dst = os.path.join(target, cat, name)
if os.path.exists(src):
try:
shutil.move(src, dst)
moved += 1
except Exception as e:
errors.append(f"{name}: {e}")
print(f"배치{N} 완료: {moved}개 이동, 오류 {len(errors)}개")
for e in errors: print(e)
import os, json
with open('/tmp/sort_files_plan.json', encoding='utf-8') as f:
data = json.load(f)
target = data['target']
cats = ["이미지","동영상","설치파일","폰트","개인문서","업무문서","_Office임시"]
total = 0
print("=== 정리 완료 ===")
for c in cats:
path = os.path.join(target, c)
n = len(os.listdir(path)) if os.path.exists(path) else 0
total += n
print(f" {c}/: {n}개")
root = [x for x in os.listdir(target) if not x.startswith('.') and x not in cats]
print(f"총 {total}개 이동 완료 | 루트 잔여: {len(root)}개")
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub juhee815/test --plugin creative