
    (<i'Z                        d Z ddlZddlZddlZddlZddlmZ ddlmZ ddlm	Z	 h dZ
h dZdZdZd	Zd
Zh dZh dZdZ G d d      Zdedeeee	f      fdZdedeeee	f      fdZdedeeee	f      fdZdedeeee	f      fdZd-deeee	f      dedeeee	f      fdZ	 	 d.deeee	f      dededz  deee	f   fdZdededededeeeeee	f      f   f
d Z	 	 	 	 	 d/d!edz  dedz  dedz  dedz  dedz  deee	f   fd"Z ded#ed$edeeee	f      fd%Z!	 d0d&eeee	f      dedeee	f   fd'Z"d(edeeee	f      d)ee   ddfd*Z#d1d+Z$e%d,k(  r e$        yy)2u  file-cleanup.py - 파일 자동 정리 스크립트.

4가지 카테고리:
1. cokacdir 업로드 파일 정리 (30일 초과 미디어)
2. .done.clear 파일 정리 (30일 초과)
3. dispatch 지시서 정리 (90일 초과)
4. 시스템 로그 정리 (60일 초과)

실행 모드:
  python3 file-cleanup.py           → dry-run (삭제 대상만 출력)
  python3 file-cleanup.py --execute → 실제 삭제
  python3 file-cleanup.py --report  → 디스크 사용량 + 정리 가능 용량
  python3 file-cleanup.py --organize            → cokacdir 파일 정리 (dry-run)
  python3 file-cleanup.py --organize --execute  → 실제 이동
    N)datetime)Path)Any>   .jpg.pdf.png.jpeg>   .py.sh.md   Z   <   >   .env	.env.keys	CLAUDE.md	MEMORY.md>   .git
.worktreesplansspecsreportsmeetingsprojectsresearchzfile-cleanup.logc                   0    e Zd ZdZdeddfdZdedefdZy)SafetyCheckeru+   파일이 보호 대상인지 확인한다.base_dirreturnNc                     || _         y N)r   )selfr   s     E/home/jay/workspace/.worktrees/task-2057-dev2/scripts/file-cleanup.py__init__zSafetyChecker.__init__;   s	         pathc                     |j                   t        v ryt        |j                        }|t        z  ryt        |      }dD ]  }||v s y y)u=   주어진 경로가 보호 대상이면 True를 반환한다.T)zmemory/reportszmemory/researchzmemory/specszmemory/meetingszmemory/plansF)namePROTECTED_FILENAMESsetpartsPROTECTED_DIR_PARTSstr)r"   r&   r+   path_str	protecteds        r#   is_protectedzSafetyChecker.is_protected>   sY     99++ DJJ&& t9q 	IH$	 r%   )__name__
__module____qualname____doc__r   r$   boolr0    r%   r#   r   r   8   s+    5! !$ ! $ r%   r   workspace_dirr   c           	         | j                         sg S g }t        j                         j                         }| j	                         D ]   }|j                         s|j                  d      D ]  }|j                         s|j                  j                         }|j                  t        v rA|j                  j                         t        v rb|t        vrk|j                         j                  }||z
  }|dz  }t!        |      t"        k  r|j%                  t'        |      |j                         j(                  |dd         |S )u   cokacdir workspace에서 30일 초과 미디어 파일을 찾아 반환한다.

    Args:
        workspace_dir: ~/.cokacdir/workspace/ 경로

    Returns:
        삭제 후보 목록. 각 항목은 path, size_bytes, days_old, category 포함.
    *Q cokacdirr&   
size_bytesdays_oldcategory)existsr   now	timestampiterdiris_dirrglobis_filesuffixlowerr(   r)   COKACDIR_PROTECTED_EXTENSIONSCOKACDIR_MEDIA_EXTENSIONSstatst_mtimeintCOKACDIR_THRESHOLD_DAYSappendr-   st_size)	r7   
candidatesrA   project_dir	file_pathrG   mtimeage_secondsr>   s	            r#    find_cokacdir_cleanup_candidatesrV   W   s8    !	')J
,,.
"
"
$C %,,. !!#$**3/ 	I$$&%%++-F~~!44%%'+HH66NN$--E+K"i0H8} 77	N"+.."2":": ( *	'	> r%   
events_dirc                    | j                         sg S g }t        j                         j                         }| j	                         D ]  }|j                         s|j                  j                  d      s0|j                         j                  }||z
  }|dz  }t        |      t        k  rg|j                  t        |      |j                         j                  |dd        |S )u   memory/events/*.done.clear 파일 중 30일 초과 파일을 찾아 반환한다.

    Args:
        events_dir: memory/events/ 경로

    Returns:
        삭제 후보 목록.
    z.done.clearr:   
done_clearr<   )r@   r   rA   rB   rC   rF   r(   endswithrK   rL   rM   DONE_CLEAR_THRESHOLD_DAYSrO   r-   rP   )rW   rQ   rA   rS   rT   rU   r>   s          r#   find_done_clear_candidatesr\      s     	')J
,,.
"
"
$C'') 
	  "~~&&}5 ))Ek),x=55I'nn.66$(		

( r%   	tasks_dirc                    | j                         sg S g }t        j                         j                         }| j	                         D ]  }|j                         s|j                  j                  d      s0|j                  j                         dk7  rN|j                         j                  }||z
  }|dz  }t        |      t        k  r|j                  t        |      |j                         j                   |dd        |S )u   memory/tasks/dispatch-*.md 파일 중 90일 초과 파일을 찾아 반환한다.

    Args:
        tasks_dir: memory/tasks/ 경로

    Returns:
        삭제 후보 목록.
    z	dispatch-r   r:   dispatchr<   )r@   r   rA   rB   rC   rF   r(   
startswithrG   rH   rK   rL   rM   DISPATCH_THRESHOLD_DAYSrO   r-   rP   )r]   rQ   rA   rS   rT   rU   r>   s          r#   find_dispatch_candidatesrb      s     	')J
,,.
"
"
$C&&( 
	  "~~((5!!#u, ))Ek),x=33I'nn.66$&		

, r%   logs_dirc                 t   | j                         sg S g }t        j                         j                         }t        j                         j	                  dddd      j                         }| j                         D ]  }|j                         s|j                  j                         dk7  r2|j                  t        k(  rF|j                         j                  }||k\  rf||z
  }|dz  }t        |      t        k  r|j                  t!        |      |j                         j"                  |dd        |S )u  workspace/logs/*.log 파일 중 60일 초과 파일을 찾아 반환한다.
    오늘 수정된 파일(활성 로그)은 스킵한다.
    file-cleanup.log 자체는 제외한다.

    Args:
        logs_dir: workspace/logs/ 경로

    Returns:
        삭제 후보 목록.
    r   )hourminutesecondmicrosecondz.logr:   logsr<   )r@   r   rA   rB   replacerC   rF   rG   rH   r(   CLEANUP_LOG_FILENAMErK   rL   rM   LOG_THRESHOLD_DAYSrO   r-   rP   )rc   rQ   rA   today_startrS   rT   rU   r>   s           r#   find_log_candidatesrn      s    ??	')J
,,.
"
"
$C,,.((a!QR(S]]_K%%' 
	  "!!#v->>11 ))KEk),x=..I'nn.66$"		
#
4 r%   rQ   keepc                    | sg S i }| D ]5  }|j                  dd      }|j                  |g       j                  |       7 g }|j                         D ]6  \  }}t	        |      |k  rt        |d       }|j                  ||d        8 |S )ue  각 카테고리에서 최소 keep개 파일을 보존하도록 후보 목록을 축소한다.

    가장 최근 파일(days_old가 가장 작은 것)을 보존 대상으로 선택한다.

    Args:
        candidates: 삭제 후보 목록
        keep: 카테고리별 보존할 최소 파일 수

    Returns:
        축소된 삭제 후보 목록.
    r?   unknownc                     | d   S )Nr>   r6   )xs    r#   <lambda>z$apply_minimum_keep.<locals>.<lambda>5  s
    1Z= r%   )keyN)get
setdefaultrO   itemslensortedextend)rQ   ro   by_categoryccatresultrx   sorted_itemss           r#   apply_minimum_keepr     s     	 46K 2eeJ	*sB'..q12 $&F!'') +
Uu:e)@Al45)*+ Mr%   dry_runcleanup_log_pathc           	         g }d}|s| D ][  }t        |d         }|j                         s"	 |j                          |j                  |d          ||j	                  dd      z  }] |r|r|j                  j                  dd       t        j                         j                  d      }|j                  d	d
      5 }	|	j                  d| dt        |       d       |D ]  }
|	j                  d|
 d        	 ddd       t        |      |||dS # t
        $ r%}t        j                  d|d   |       Y d}~5d}~ww xY w# 1 sw Y   JxY w)u  후보 파일들을 삭제하거나(execute) 목록만 출력한다(dry-run).

    Args:
        candidates: 삭제 후보 목록
        dry_run: True면 실제 삭제 안 함 (기본값)
        cleanup_log_path: 삭제 로그 파일 경로 (None이면 기본 경로 미사용)

    Returns:
        결과 딕셔너리: deleted_count, dry_run, deleted_files, total_size_bytes
    r   r&   r=   u   삭제 실패: %s - %sNTparentsexist_okz%Y-%m-%d %H:%M:%Sazutf-8)encodingz
[z] Cleanup executed - z files deleted
z  DELETED: 
)deleted_countr   deleted_filestotal_size_bytes)r   r@   unlinkrO   rv   OSErrorloggingwarningparentmkdirr   rA   strftimeopenwritery   )rQ   r   r   r   
total_sizer}   perB   ffps              r#   execute_cleanupr   A  s_     "MJ 	LAQvYAxxzLHHJ!((63!%%a"88J	L ##))$)F //0CDI!&&sW&= 2#i[(=c->P=QQabc' 2BGGk"R0122 ]+&&	   LOO$<aiKKL2 2s#   9D>>E	ED>>EEcokacdir_workspacec                 X    t        |       t        |      t        |      t        |      dS )u=  4가지 카테고리의 모든 정리 후보를 수집한다.

    Args:
        cokacdir_workspace: ~/.cokacdir/workspace/ 경로
        events_dir: memory/events/ 경로
        tasks_dir: memory/tasks/ 경로
        logs_dir: workspace/logs/ 경로

    Returns:
        카테고리별 후보 딕셔너리.
    )r;   rY   r_   ri   )rV   r\   rb   rn   r   rW   r]   rc   s       r#   collect_all_candidatesr   t  s/    $ 55GH0<,Y7#H-	 r%   r   c           
         | at        t        j                  j                  dt	        t        t
              j                         j                  j                                    } ||n| dz  }||n
t        d      }||n| dz  dz  }||n| dz  dz  }t        j                  | j                         rt	        |       nd      }	|	j                  dz  }
|	j                  dz  }|	j                  dz  }t        |
d	      t        |d	      t        |d	      d
}t        ||||      }t!        d |j#                         D              }t        |dz  d	      }|||dS )u  현재 디스크 사용량과 정리 가능 용량을 보고한다.

    Args:
        base_dir: 기본 디렉토리 (None이면 /home/jay/workspace 사용)
        logs_dir: 로그 디렉토리 (None이면 base_dir/logs 사용)
        cokacdir_workspace: cokacdir workspace 경로
        events_dir: events 디렉토리 경로
        tasks_dir: tasks 디렉토리 경로

    Returns:
        disk_usage, cleanup_candidates, total_reclaimable_mb 포함 딕셔너리.
    WORKSPACE_ROOTri   z/home/jay/.cokacdir/workspacememoryeventstasks/      )total_mbused_mbfree_mbr   c              3   N   K   | ]  }|D ]  }|j                  d d         ywr=   r   Nrv   ).0rx   r}   s      r#   	<genexpr>z"generate_report.<locals>.<genexpr>  s*     a[`aVWaeeL!,a,as   #%)
disk_usagecleanup_candidatestotal_reclaimable_mb)r   osenvironrv   r-   __file__resolver   shutilr   r@   totalusedfreeroundr   sumvalues)r   rc   r   rW   r]   	_logs_dir_cokacdir_ws_events_dir
_tasks_dirrK   r   r   r   r   all_candidatestotal_bytesr   s                    r#   generate_reportr     s_   & 

'7T(^=S=S=U=\=\=c=c9def$0h6GI);)G%TRqMrL * 6*Hx<ORZ<ZK'3H9Lw9VJ hoo.?S]SIDzz[)Hii;'Gii;'G (A&!$!$J ,'	N a.:O:O:QaaK !<a@ !, 4 r%   uploads_baseprojects_basec                    | j                         sg S g }t        j                         j                  d      }| j	                         D ]  }|j                         s|j                  d      D ]  }|j                         s|j                  j                         }|t        vr7|j                  t        v rJ||z  |j                  z  }d| d}	|j                  t        |      t        |      |	d         |S )uw  cokacdir workspace의 미디어 파일을 적절한 위치로 이동할 후보를 찾는다.

    Args:
        cokacdir_workspace: ~/.cokacdir/workspace/ 경로
        uploads_base: /home/jay/workspace/uploads/ 기본 경로
        projects_base: /home/jay/projects/ 경로

    Returns:
        이동 후보 목록. 각 항목은 source, destination, reason 포함.
    z%Y-%mr9   u#   기본 업로드 폴더로 이동 ())sourcedestinationreason)r@   r   rA   r   rC   rD   rE   rF   rG   rH   rJ   r(   r)   rO   r-   )
r   r   r   rQ   
year_monthrR   rS   rG   r   r   s
             r#   find_organize_candidatesr     s     $$&	')J((1J)113 !!#$**3/ 	I$$&%%++-F66~~!44 '3innDK::,aHF!)n#&{#3$	0 r%   movesc                    g }|s| D ]  }t        |d         }t        |d         }|j                         s0|j                  j                  dd       	 t	        j
                  t        |      t        |             |j                  |d           t        |      ||dS # t        $ r"}t        j                  d|||       Y d}~d}~ww xY w)u  이동 목록을 실행하거나(execute) 목록만 출력한다(dry-run).

    Args:
        moves: 이동 후보 목록
        dry_run: True면 실제 이동 안 함 (기본값)

    Returns:
        결과 딕셔너리: moved_count, dry_run, moved_files
    r   r   Tr   u   이동 실패: %s -> %s - %sN)moved_countr   moved_files)r   r@   r   r   r   mover-   rO   r   r   r   ry   )r   r   r   msrcdstr   s          r#   execute_organizer     s      K 		QAq{#Cq'(Czz|

   =QKKC#c(3&&q{3		Q ;'"   QOO$BCaPPQs   <B""	C+CCtitlecolumnsc                 n   t        dd        t        d|         t        d        |st        d       y|D ci c]*  t        t              t        fd|D                    , c}ddj                  fd|D              z   }t        |       t        ddt	        j                               d	t        |      z  z   z  z          |D ]+  ddj                  fd
|D              z   }t        |       - t	        d |D              dz  }t        dt        |       d|dd       yc c}w )u5   후보 목록을 테이블 형식으로 출력한다.r   F======================================================================  u     (정리 대상 없음)Nc           	   3   f   K   | ](  }t        t        |j                  d                    * yw N)ry   r-   rv   )r   r}   cols     r#   r   z_print_table.<locals>.<genexpr>6  s%     (VaSsB-@)A(Vs   .1c              3   F   K   | ]  }|j                  |           y wr!   )ljust)r   r   
col_widthss     r#   r   z_print_table.<locals>.<genexpr>7  s     LScii
38Ls   !-r   c              3   x   K   | ]1  }t        j                  |d             j                  |          3 ywr   )r-   rv   r   )r   r   r}   r   s     r#   r   z_print_table.<locals>.<genexpr>;  s/     ]cs155b>288CI]s   7:c              3   @   K   | ]  }|j                  d d        ywr   r   r   r}   s     r#   r   z_print_table.<locals>.<genexpr>>  s     >a155q)>   r   u   
  총    개 파일, .2f MB)printmaxry   joinr   r   )	r   rQ   r   r   headerrowr   r}   r   s	      `   @@r#   _print_tabler   (  s#    
Bxj/	Bug,	XJ() dkk\_#s3s8S(V:(V%VWWkJDIILGLLLF	&M	$J--/01s7|3CCD
DE TYY]U\]]]c
 >:>>+NH	HS_%\(3s
CD ls   /D2c            	      &   t        j                  dt         j                        } | j                  ddd       | j                  ddd       | j                  d	dd
       | j	                         }t        j                         }|dz  }|dz  dz  }|dz  dz  }|dz  dz  }|dz  }|dz  }|dz  }	|t        z  }
|j                   }|j                  rt        |||||      }t        d       t        d       t        d       |d   }t        d|d   dd       t        d|d   dd       t        d|d   dd       t        d |d!   d"d       |d#   }|j                         D ]   \  }}t        d$| d%t        |       d&       " y'|j                  rt        |||	(      }|sd)nd*}t        d+| d,       |st        d-       y'|D ]+  }t        d.|d/           t        d0|d1    d2|d3    d4       - |s t!        |d56      }t        d7|d8    d9       y't        d:t        |       d;       y't#        ||||<      }g }|j                         D ]6  \  }}t%        |d=>      }|j'                  |       t)        d?| d@|g dA       8 |sd)nd*}t+        dB |D              dCz  }t        dDd        t        d.| dEt        |       dF|d"d       t        d       |s!t-        |d5|
G      }t        dH|dI    d&       y't        dJ       y')Ku   CLI 진입점.u!   파일 자동 정리 스크립트)descriptionformatter_classz	--execute
store_trueu/   실제 삭제/이동 실행 (없으면 dry-run))actionhelpz--reportu3   디스크 사용량 및 정리 가능 용량 보고z
--organizeu    cokacdir workspace 파일 정리	workspacez	.cokacdirr   r   r   ri   uploadsr   )r   rc   r   rW   r]   zG
======================================================================u     디스크 사용량 보고r   r   u     전체:  r   z>10.2fr   u     사용:  r   u     여유:  r   u   
  정리 가능 총 용량: r   r   r   z    [z] u
   개 파일N)r   r   r   u   실행 모드u   DRY-RUN 모드z
[organize - ]u     이동 대상 파일 없음.r   r   z    -> r   z  (r   r   F)r   z
  r   u   개 파일 이동 완료.z
  (dry-run) u8   개 파일 이동 예정. --execute 로 실행하세요.r      )ro   [u   ] 정리 대상)r&   r>   r=   c              3   @   K   | ]  }|j                  d d        ywr   r   r   s     r#   r   zmain.<locals>.<genexpr>  s     <a155q)<r   r   r   u   : 총 r   )r   r   u     삭제 완료: r   uW     --execute 플래그 없음. 실제 삭제를 원하면 --execute 를 추가하세요.)argparseArgumentParserRawDescriptionHelpFormatteradd_argument
parse_argsr   homerk   executereportr   r   rx   ry   organizer   r   r   r   r{   r   r   r   )parserargsr  r   r   rW   r]   rc   r   r   r   r   r  duall_cr~   rx   r   
mode_labelr   r   r   all_flatsafer   s                            r#   mainr  G  s   $$7 <<F >  
 B  
 /  
 D 99;D{"I+k9X%0JH$w.I6!Hy(L:%M"66,,G {{ 1!
 	o,-hL!BzN62#67ByM&156ByM&156.v6L/Mc.RRUVW+,++- 	9JCE#bUJ78	9 }}(1%'

 -4_9I
zl!,-23 	AABq{m$%GAm,-S8Q?@	A %eU;FD.//HIJ 	 N3u:,.fgh ,-	N &(H$**, 

U!%a0uO$.	

 )05EJ<8<<LH	Bxj/	Bzl&X|HS>
MN	(O 5K[\!&"9!:*EFghr%   __main__)r   )TN)NNNNN)T)r   N)&r4   r   r   r   r   r   pathlibr   typingr   rJ   rI   rN   r[   ra   rl   r)   r,   rk   r   listdictr-   rV   r\   rb   rn   rM   r   r5   r   r   r   r   r   r   r  r1   r6   r%   r#   <module>r     s      	     >  5     E 	  *  >/D /T$sCx.=Q /n#4 #Dc3h4H #V% %d38n1E %Z,$ ,4S#X+? ,h4S#X#7 s 4PTUXZ]U]P^K_ R $(+T#s(^$++ Tk+ 
#s(^	+f  	
 
#tDcN#
#$> ! &*"!7Tk7Tk7 t7 t	7
 d{7 
#s(^7~--- - 
$sCx.	-d S#X 
#s(^NEET#s(^$E #YE 
	E>mi` zF r%   