Skip to content

Latest commit

 

History

History
255 lines (184 loc) · 14.2 KB

File metadata and controls

255 lines (184 loc) · 14.2 KB
name cleaner
description macOS 로컬 파일을 안전하게 정리하는 스킬. 형식별 분류, 파일명·내용 기반 주제/프로젝트 귀속, 완전중복·폴더복제 탐지, 버전 난립 정리, 민감파일 점검을 수행하고, 반드시 계획을 먼저 보여준 뒤 승인받아 실행하며 한 줄 명령으로 100% 되돌린다. 삭제 기능은 존재하지 않는다(격리 폴더 또는 휴지통만). 트리거 — "다운로드 폴더 정리", "파일 정리해줘", "바탕화면 정리", "중복 파일 찾아", "중복 파일 정리", "용량 차지하는 파일", "디스크 정리", "같은 파일 여러 개", "폴더가 두 개로 복사됐어", "이 파일들 분류해줘", "확장자별로 정리", "프로젝트별로 자료 모아줘", "이 자료 어느 프로젝트 거야", "v1 v2 v3 정리", "최종 최종본 정리", "옛날 버전 치워", "어떤 파일이 쌓여있는지 보여줘", "파일 정리 계획 보여줘", "정리 되돌려", "undo", "방금 정리 취소", "정리 다시", "다른 폴더도 정리", "격리한 거 휴지통으로", "민감한 파일 어디 있나", "인감증명서 신분증 같은 거 찾아", "cleaner". 후속 작업("계획 수정", "이 항목만 빼고", "다시 스캔", "범위 넓혀서", "N번 제외", "이 프로젝트로 다시 분류")도 모두 이 스킬. 단순히 파일 하나를 찾는 것은 Spotlight로 충분하므로 이 스킬을 쓰지 않는다. 파일 내용을 읽어 요약·집필하는 작업, PDF/PPTX 편집·생성은 각각의 전용 스킬을 쓴다.

cleaner — 로컬 파일 정리

이 스킬이 존재하는 이유

Finder와 Spotlight는 이름을 아는 파일 하나를 찾는 일을 이미 1초에 해낸다. 그건 다시 만들지 마라. 이 스킬이 대신하는 것은 사람이 눈으로 할 수 없는 관계를 찾는 일이다.

  • 279개짜리 폴더 두 개가 내용이 같은지 — 사람이 대조하려면 몇 시간이다.
  • 어떤 v7이 어떤 최종의 후속인지 — 파일명만 보면 알 수 없다.
  • 이 PDF가 어느 프로젝트의 자료인지 — 프로젝트 목록을 알아야 판단된다.
  • HWP·HWPX 안에 무엇이 쓰였는지 — Spotlight는 한컴 파일을 전혀 색인하지 않는다.

이 네 가지에만 집중하라. 확장자별 폴더 분류 같은 것은 가치가 거의 없다. 서술적인 파일명을 쓰는 사용자에게 PDF/ 폴더는 정보를 늘리지 않고 경로만 길게 만든다.

절대 원칙

  1. 삭제하지 않는다. 스크립트에 rm·os.remove·rmtree가 존재하지 않는다. "삭제"는 격리 폴더로의 이동이고, 휴지통은 사용자가 따로 요청할 때만이다.
  2. 계획 없이 실행하지 않는다. 계획을 보여주고 승인을 받은 뒤에만 옮긴다. 승인 토큰은 계획 파일의 해시라서, 승인 후 계획을 고치면 실행이 거부된다.
  3. 되돌릴 수 있어야 옮긴다. 모든 실행은 매니페스트를 남기고, 그것만으로 원복된다.
  4. 스코프 밖으로 나가지 않는다. 사용자가 지정한 디렉토리 하나가 전부다. ~, /, /Users, /Volumes는 스코프로 지정할 수 없다.
  5. 애매하면 옮기지 않는다. 판단이 서지 않는 파일은 제자리에 두고 목록으로 보고하라. 잘못 옮긴 파일은 물리적으로 무사해도 사용자가 못 찾는다 — 그게 이 작업의 가장 흔한 실패다.

워크플로

scripts/의 도구는 전부 결정적이다. LLM은 4단계의 판단 부분에만 쓴다. 스캔·해시·중복탐지·이동은 파이썬이 훨씬 빠르고 정확하므로 직접 하지 마라.

0단계 — 범위 확정 (사용자에게 반드시 묻는다)

어느 디렉토리를 정리할지 확정한다. 기본 후보는 ~/Downloads, ~/Desktop, ~/Documents다. 한 번의 실행은 root 하나만 다룬다.

~/Pictures를 요청받으면 거절하고 이유를 설명하라 — 그 안의 파일은 거의 전량이 Photos Library.photoslibrary 패키지 내부이며, 파일시스템에서 건드리면 사진 라이브러리가 깨진다. 사진 정리는 사진 앱에서 해야 한다.

1단계 — 스캔 (읽기 전용)

python3 scripts/scan.py <대상> -o _work/inventory.jsonl --max-depth -1

패키지(.app/.photoslibrary/.rtfd)는 파일 1개로, 프로젝트 디렉토리(.git· package.json 보유)는 불가분 단위로 취급하고 내부에 진입하지 않는다. 심볼릭 링크는 따라가지 않고, iCloud 미다운로드 파일은 열지 않는다. 1,500개에 0.2초면 끝난다.

2단계 — 관계 탐지 (읽기 전용)

python3 scripts/dedup.py _work/inventory.jsonl -o _work/dupes.json
python3 scripts/classify.py _work/inventory.jsonl -o _work/classified.jsonl

dedup.py는 세 가지를 낸다 — 완전중복 그룹, 폴더 복제쌍, 버전 계보. classify.py는 홈의 프로젝트 디렉토리 이름과 그 안의 파일명에서 어휘를 만들어 파일을 귀속시킨다. 규칙으로 3~4할이 확정되고 나머지가 4단계로 넘어간다.

3단계 — 결과를 사용자에게 보고

숫자를 먼저 보여라. "중복 331그룹 / 잉여 365개 / 2.1GB", "폴더 복제쌍 8개", "버전 계보 70개". 그다음 가장 큰 덩어리 몇 개를 구체적 이름으로 제시한다. 사용자는 전체 목록이 아니라 무엇이 문제인지를 알고 싶어 한다.

4단계 — 미해결분 판단 (LLM이 필요한 유일한 지점)

classify.pyunresolved·ambiguous로 남긴 항목의 파일명만 읽고 귀속을 판단하라. 이 사용자층의 파일명은 서술적이라 파일명만으로 대부분 판별된다.

내용을 여는 것은 파일명으로 도저히 알 수 없을 때뿐이다. 스크린샷, KakaoTalk_Photo_*, UUID 이름 같은 것들이다. 그런 것은 대개 열어봐도 용도를 알 수 없으니, 억지로 분류하지 말고 _미분류로 남기는 편이 낫다.

내용이 정말 필요하면:

python3 scripts/extract.py --from-inventory _work/inventory.jsonl --only pdf,hwp,hwpx --jsonl

pdf·hwp·hwpx·pptx·docx·xlsx·md를 앞부분만 뽑는다. 1,000개에 30초 안쪽이다. HWP·HWPX는 Spotlight가 전혀 색인하지 않으므로 mdfind로는 찾을 수 없다 — 한글 문서가 많은 환경에서 이 추출기가 대체 불가능한 이유다.

판단 결과는 classified.jsonlproject·confidence 필드를 채워 반영한다.

미해결분이 200개를 넘으면 general-purpose 서브에이전트에 맡겨 메인 컨텍스트를 지켜라. 역할 정의는 references/agent-file-classifier.md에 있으니 그 내용을 프롬프트로 넘긴다. 200개 이하면 직접 판단하는 편이 왕복 비용이 적다.

5단계 — 계획 생성

python3 scripts/plan.py --inventory _work/inventory.jsonl \
  --classified _work/classified.jsonl --dupes _work/dupes.json \
  --root <대상> -o _work/plan.json --do archive,quarantine,version

--do로 작업을 고른다. 한 번에 한 종류만 하는 편이 승인 단위가 선명하다. 중복 정리만 원하면 --do quarantine이다.

6단계 — 감사 후 승인 (건너뛸 수 없다)

계획을 사용자에게 보이기 전에 적대적으로 검수한다. 역할 정의는 references/agent-cleanup-auditor.md에 있다. 계획이 100건을 넘거나 격리·버전 작업이 섞여 있으면 반드시 서브에이전트로 돌려라 — 계획을 만든 쪽과 검수하는 쪽이 같으면 자기 논리의 허점을 못 본다.

감사가 reject면 계획을 다시 만들고, revise면 지목된 항목을 빼고 다시 만든다. pass여야만 다음으로 간다.

python3 scripts/apply.py verify _work/plan.json

토큰과 사전검사 결과가 나온다. 사용자에게 반드시 함께 보여줄 것: 총 건수 · 총 용량 · 스코프 root 절대경로 · 새로 생기는 폴더 수 · 스킵되는 항목과 그 사유 · 되돌리기 명령 한 줄.

그리고 명시적 승인을 받아라. "알아서 해줘"는 승인이 아니다. 계획을 보고 "이대로 진행"이라고 답해야 승인이다.

7단계 — 실행

python3 scripts/apply.py run _work/plan.json --token <토큰> --runs-dir _work/_runs

파일은 os.link+unlink로 옮긴다 — 대상이 이미 있으면 실패하므로 덮어쓰기가 원천적으로 불가능하고, 원본은 새 자리가 확인된 뒤에만 사라진다. 이름이 겹치면 (2)로 비켜간다. 모든 이동은 디스크에 먼저 기록되므로 중간에 끊겨도 어디까지 갔는지 남는다.

8단계 — 지도 남기기

정리한 폴더 최상위에 _CLEANER_MAP.md를 남겨라. 새 폴더 구조 트리와 "무엇이 어디로 갔는지"를 사람이 읽을 수 있게 적는다. 물리적으로 무손실이어도 사용자가 못 찾으면 실패한 정리다.

되돌리기

python3 scripts/apply.py undo _work/_runs/<run_id>/manifest.json
python3 scripts/apply.py undo _work/_runs/<run_id>/          # 디렉토리도 된다

역순으로 재생한다. 정리 이후 내용이 바뀐 파일, 원래 자리에 다른 파일이 생긴 경우는 건드리지 않고 사유와 함께 보고한다. 이번 실행이 만든 빈 폴더만 정리하고 원래 있던 폴더는 남긴다.

격리한 것을 휴지통으로 (별도 요청 시에만)

격리 폴더를 사용자가 직접 확인한 뒤 요청할 때만 수행한다. 같은 실행에서 연달아 하지 마라 — 승인 단위가 흐려진다.

/usr/bin/trash -v <경로들>

macOS의 trash--(옵션 종결자)를 지원하지 않는다. 붙이면 그것을 파일 이름으로 알고 오류를 낸다. 대신 파이썬에서 subprocess.run([...], shell=False) 로 인자 배열을 넘겨라 — 셸을 거치지 않으므로 -로 시작하는 파일명이 아닌 한 안전하고, 공백·따옴표 파일명도 그대로 통과한다.

휴지통으로 넘어가면 원위치 정보는 매니페스트에만 남는다. 반드시 먼저 고지하라.

하지 말아야 할 것

하지 말 것 이유
확장자별 폴더로 전량 분류 확장자만으로 처분 가능한 파일은 극소수다. PDF/ 폴더는 정보를 늘리지 않는다
mtime 기준 자동 아카이브 다운로드 폴더는 활성 작업면이다. 오래된 것이 덜 중요하다는 보장이 없다
파일명 자동 개명 사용자의 260812_·v7·3교본 같은 자체 규칙을 파괴하고 본인 기억과 어긋나게 만든다
모든 파일 내용 파싱 필요한 비율이 낮은데 비용과 오분류 위험이 크다. 파일명이 먼저다
"최종"이 최신이라는 가정 v6최종의 해시가 다른 경우가 흔하다. 시각·해시로 판단하라
프로젝트 디렉토리 내부 재배치 git 워킹트리가 깨지고 미커밋 작업이 사라진다
폴더 안 대량 원자료 재편 부처별·출처별로 이미 정돈된 것을 흩뜨리는 일이다. 중복 폴더만 제거하라

민감 파일 점검 (분류가 아니라 보고다)

인감증명서·신분증 사본·복구 코드·계약서·소송자료가 다운로드 폴더에 평문으로 남아 있는 일이 흔하다. 발견하면 옮기지 말고 목록으로 보고하라. 어디로 옮길지는 사용자만 결정할 수 있다.

자가 점검

실행기를 수정했다면 사용자 파일에 쓰기 전에 반드시:

python3 scripts/selftest.py

23개 케이스를 샌드박스에서 검증한다 — NFD 한글·공백·따옴표·개행 파일명, 이름 충돌, 위조 토큰, 승인 후 계획 변조, 심볼릭·순환 링크, 하드링크, 권한 거부, 락 회수, 강제종료 후 저널 복원, 그리고 hdiutil 로 실제 디스크 이미지를 만들어 크로스볼륨 이동과 대소문자 구분 파일시스템까지. 전 항목 통과가 아니면 사용자 파일을 건드리지 마라.

팀 구성

이 스킬은 대부분 결정적 스크립트로 돌아가고, 판단이 필요한 두 지점에만 사람 역할을 둔다. 스캔·해시·중복탐지·이동에 LLM을 쓰면 느리고 부정확하고 비싸다.

역할 언제 정의
귀속 판정관 4단계, 미해결분 200개 초과 시 references/agent-file-classifier.md
계획 감사관 6단계, 계획 100건 초과 시 references/agent-cleanup-auditor.md

두 역할은 서로 통신할 필요가 없다(판정 → 계획 → 감사의 단방향 파이프라인). 그러므로 팀이 아니라 서브 에이전트로 호출하고, 결과는 파일로 주고받는다. 프로젝트 사본은 .claude/agents/에 있다.

테스트 시나리오

정상 흐름 — "다운로드 폴더 정리해줘" → 범위 확인 → 스캔 → 중복·귀속 탐지 → 숫자 보고 → 미해결분 판정 → 계획 생성 → 감사 통과 → 계획 제시 → 승인 → 실행 → 지도 생성 → 되돌리기 명령 안내

에러 흐름 — 승인 후 사용자가 "3번 항목은 빼줘" → 계획을 다시 만든다(토큰이 바뀌므로 이전 승인은 무효다) → 재검증 → 재승인. 이전 토큰으로 실행을 시도하면 실행기가 거부한다. 그게 설계대로 동작한 것이니 우회하지 말고 승인을 다시 받아라.

에러 흐름 — 실행 중 중단(강제종료·전원 차단 포함) → undo실행 디렉토리를 넘겨라. 매니페스트가 없으면 저널에서 복원한다.

python3 scripts/apply.py undo _work/_runs/<run_id>/

매니페스트는 정상 종료 때만 쓰이므로, SIGKILL 로 죽으면 이미 옮겨진 파일이 수백 건이어도 되돌릴 입력이 없어진다. 저널은 이동 전마다 fsync 되므로 그것만으로 복원된다(3,000건 계획을 중간에 죽여 전량 복원 검증). 죽은 실행이 남긴 .cleaner.lock 은 다음 실행이 PID 생존을 확인하고 회수한다.

복원 후에는 이어서 하지 말고 처음부터 다시 하라 — 스캔 시점 스냅샷이 이미 어긋나 있다.

참고

  • 사고 시나리오·방어 설계 전문: references/safety.md
  • 성능·포맷별 추출 경로 실측치: references/capabilities.md