
    i9                        d Z ddlZddlZddlZddlZddlZddlmZmZ ddlm	Z	 ddl
mZ dZdZdZd	e	d
eeef   fdZde	d
eeef   fdZefde	ded
eeeef      fdZded
efdZdee	   d
eeeef      fdZdeeef   deeeef      deeeef      d
ee   fdZde	d
e	dz  fdZefde	d	e	dz  dee	   ded
eeef   f
dZddZedk(  r e        yy)u   memory-janitor.py - Letta #5 self_improvement 메모리 관리 도구.

MEMORY.md 크기 모니터링, memory/ 하위 파일 크기 리포트,
30일 이상 미참조 항목 감지, 중복 파일/유사 내용 감지.
    N)datetime	timedelta)Path)Any      
   memory_md_pathreturnc                 ,   | j                         st        |       dt        ddS 	 | j                  dd      }t	        |j                               }|t        k  rdnd}t        |       |t        |dS # t        $ r t        |       dt        ddcY S w xY w)	u   MEMORY.md 줄 수를 체크하고 상태를 반환한다.

    Args:
        memory_md_path: MEMORY.md 파일 경로

    Returns:
        dict with keys: file, line_count, limit, status
        status: "OK" | "WARNING" | "NOT_FOUND"
    r   	NOT_FOUNDfile
line_countlimitstatuszutf-8replace)encodingerrorsOKWARNING)existsstr
LINE_LIMIT	read_textlen
splitlinesOSError)r
   textr   r   s       G/home/jay/workspace/.worktrees/task-2116-dev1/scripts/memory_janitor.pycheck_memory_mdr!      s       "'!	
 	

'''K*+
#z1y'$	
 	
  
'!	
 	

s   AA3 3BB
memory_dirc                 p   | j                         r| j                         sddi g dS d}d}i }g }	 | j                  d      D ]  }|j                         s	 |j	                         j
                  }|dz  }||z  }	 t        |j                  |       j                        }||vrddd||<   ||   dxx   dz  cc<   ||   dxx   |z  cc<   |j                  t        |      |d	        	 t        |d
 d      dt         }||||dS # t        $ r Y w xY w# t        $ r d}Y w xY w# t        $ r Y Hw xY w)u   memory/ 하위 전체 파일 크기를 집계한다.

    Args:
        memory_dir: memory 디렉토리 경로

    Returns:
        dict with keys: total_files, total_size_bytes, by_directory, largest_files
    r   )total_filestotal_size_bytesby_directorylargest_files*   .)files
size_bytesr+   r,   )pathr,   c                     | d   S )Nr,    )xs    r    <lambda>z(aggregate_memory_files.<locals>.<lambda>o   s
    AlO     T)keyreverseN)r   is_dirrglobis_filestatst_sizer   r   relative_toparent
ValueErrorappendsortedTOP_FILES_COUNT)	r"   r$   r%   r&   	all_filesentrysize
rel_parentr'   s	            r    aggregate_memory_filesrD   =   s    j&7&7&9 !	
 	
 K.0L&(I%%c* 	GE==?zz|++ 1K$! !2!2:!>!E!EF
 -56a+HZ($W-2-$\2d:2c%jEF-	G6 9*CTRScTcdM #,$&	 1    ! 
!  sZ   %D) D	1
D) <$D AD) 		DD) DD) D&#D) %D&&D) )	D54D5daysc                    | j                         r| j                         sg S t        j                         }g }	 | j	                  d      D ]  }|j                         s	 |j                         j                  }t        j                  |      }||z
  j                  }||kD  sY|j                  t        |      |j                  d      |d        	 |S # t        $ r Y w xY w# t        $ r Y |S w xY w)u  days일 이상 수정되지 않은 파일 목록을 반환한다 (삭제하지 않고 제안만).

    Args:
        memory_dir: memory 디렉토리 경로
        days: stale 기준 일수 (기본 30일)

    Returns:
        list of dict with keys: path, last_modified, days_stale
    r(   z%Y-%m-%d)r-   last_modified
days_stale)r   r5   r   nowr6   r7   r8   st_mtimer   fromtimestamprE   r=   r   strftime)r"   rE   rI   stalerA   mtimemodified_dtrH   s           r    detect_stale_filesrP   y   s     j&7&7&9	
,,.C"$E%%c* 	E==?

-- #007K +11JD  #E
)4)=)=j)I&0	. L%    Ls;   %C%  C:)C% $/C% 	C"C% !C""C% %	C21C2namec                 v    t        |       j                  }t        j                  dd|      j	                         S )uG   파일명에서 하이픈/언더스코어를 제거해 정규화한다.z[-_] )r   stemresublower)rQ   rT   s     r    _normalize_namerX      s-     :??D66'2t$**,,r2   	scan_dirsc                    | sg S g }| D ]\  }|j                         r|j                         s$	 |j                  d      D ]$  }|j                         s|j	                  |       & ^ g }t               }i }|D ]4  }t        |j                        }||vrg ||<   ||   j	                  |       6 |j                         D ]  \  }}	t        |	      dk  r|	D ch c]  }|j                   }
}t        |
      dk  r=|	D cg c]  }t        |       }}t        |      }||vse|j                  |       |j	                  |dd        i }|D ]+  }|j                  }||vrg ||<   ||   j	                  |       - |j                         D ]  \  }}t        |      dk  ri }|D ]:  }	 |j                         j                   }||vrg ||<   ||   j	                  |       < |j                         D ]a  \  }}	t        |	      dk  r|	D cg c]  }t        |       }}t        |      }||vs=|j                  |       |j	                  |dd       c  |S # t
        $ r Y ^w xY wc c}w c c}w # t
        $ r Y w xY wc c}w )u   파일명 유사도 및 같은 디렉토리 내 동일 크기 파일을 감지한다.

    Args:
        scan_dirs: 검색할 디렉토리 목록

    Returns:
        list of dict with keys: files, reason
        reason: "similar_name" | "same_size"
    r(      similar_name)r+   reason	same_size)r   r5   r6   r7   r=   r   setrX   rQ   itemsr   r   	frozensetaddr;   r8   r9   )rY   r@   scan_dirrA   
duplicates
seen_pairsname_groupsf
normalizedgroupunique_names
file_pathspair_key	dir_filesr;   r+   size_groupsrB   s                     r    detect_duplicatesro      s    	I  (9	!, ,==?$$U+,	 (*J&)eJ *,K *$QVV,
[(&(K
#J&&q)	* )..0 O
Eu:>(-.1..|q &+,c!f,
,Z(:%NN8$
nMNO )+I $" "If&  #	$ #* Pu:>-/ 	(Avvx'' ;&$&D!$$Q'	( ',,. 	PKD%5zA~*/0Q#a&0J0 ,Hz)x(!!J+"NO	PP. y  		& / -.   1s;   $III+ I0*I5J	I('I(5	J Jmemory_md_checkstale_filesrd   c                    g }| j                  dd      }| j                  dd      }| j                  dt              }|dk(  r|j                  d| d| d	       n4|d
k(  r|j                  d| d| d       n|dk(  r|j                  d       |r!t        |      }|j                  d| d       n|j                  d       |D cg c]  }|d   dk(  s| }	}|	D ]L  }
|
d   }|D cg c]  }t	        |      j
                   }}|j                  ddj                  |              N |D cg c]  }|d   dk(  s| }}|r|j                  dt        |       d       |S c c}w c c}w c c}w )u"  분석 결과를 바탕으로 권고사항 목록을 생성한다.

    Args:
        memory_md_check: check_memory_md() 반환값
        stale_files: detect_stale_files() 반환값
        duplicates: detect_duplicates() 반환값

    Returns:
        list of recommendation strings
    r   r   r   r   r   r   u   MEMORY.md는 u   줄로 u   줄 제한 이내입니다.r   u   MEMORY.md가 u<   줄 제한을 초과했습니다. 정리가 필요합니다.u,   MEMORY.md 파일을 찾을 수 없습니다.u   30일 미참조 파일 u%   개: 아카이브를 고려하세요.u.   30일 이상 미참조 파일이 없습니다.r]   r\   r+   u   유사 파일명 발견: z / r^   u   동일 크기 파일 u-   쌍 발견: 중복 여부를 확인하세요.)getr   r=   r   r   rQ   join)rp   rq   rd   recsr   r   r   countdsimilardupr+   rg   namesr^   s                  r    generate_recommendationsr{      s    D   ;7F $$\15J4E~mJ<wug=XYZ	9	mJ<wug=yz{	;	BC K -eW4YZ[DE %FQ(~(EqFGF EG',-!a--/

50A/BCDE
 'Eq!H+*DEIE+C	N+;;hijK G . Fs   E&E&)E+0E0>E0	workspacec                     | dz  dz  }|j                         s| dz  dz  }|j                         r|S y	 |j                  d      D ]  }|c S  	 y# t        $ r Y yw xY w)u   워크스페이스 내 .claude/projects/ 하위에서 MEMORY.md를 검색한다.

    Args:
        workspace: 워크스페이스 루트 경로

    Returns:
        첫 번째로 발견된 MEMORY.md 경로, 없으면 None
    z.claudeprojectsmemoryz	MEMORY.mdN)r   r6   r   )r|   projects_dirfallbackrA   s       r    find_memory_mdr   1  s     y(:5L x'+5??O!''4 	EL	   s   A A 	AA
stale_daysc                     |t        |      }nddt        dd}t        |       }t        | |      }t	        |      }t        |||      }t        j                         j                  d      |||||dS )u\  전체 메모리 관리 리포트를 생성한다.

    Args:
        memory_dir: memory/ 디렉토리 경로
        memory_md_path: MEMORY.md 파일 경로 (None 이면 NOT_FOUND)
        scan_dirs: 중복 감지를 위한 스캔 디렉토리 목록
        stale_days: stale 기준 일수

    Returns:
        JSON 스키마에 맞는 dict
    rS   r   r   r   )rE   z%Y-%m-%dT%H:%M:%S)	timestampmemory_filesrp   rq   rd   recommendations)	r!   r   rD   rP   ro   r{   r   rI   rL   )	r"   r
   rY   r   md_check	mem_filesrM   dupsru   s	            r    generate_reportr   J  s    $ !">2 !	
 'z2I z
;E Y'D $HeT:D \\^,,-@A!# r2   c                  h   t        j                  d      } | j                  dt        t        t        j
                  j                  dt        t        t              j                         j                  j                                    d       | j                  dt        dd	       | j                  d
t        dd       | j                  dt        t        dt         d       | j                  dddgdd       | j                         }|j                  }|j                  r|j                  n|dz  }|j                   r|j                   }nt#        |      }g }|dz  }|j%                         r|j'                  |       |j%                         r|j'                  |       t)        ||||j*                        }|j,                  dk(  r"t/        t1        j2                  |dd             yt/        d|d    d       t/                |d   }t/        d|d    d |d!    d"|d#    d$|d%    d&	       t/                |d'   }	t/        d(|	d)    d*|	d+   d,d-       t/                |d.   r@t/        d/t5        |d.          d0       |d.   dd1 D ]  }
t/        d2|
d3    d4|
d5    d6        t/                |d7   rNt/        d8t5        |d7          d9       |d7   dd1 D ](  }t/        d2|d:    d d;j7                  |d<                 * t/                t/        d=       |d>   D ]  }t/        d2|         y)?u   CLI 진입점.uA   memory-janitor: Letta #5 self_improvement 메모리 관리 도구)descriptionz--workspaceWORKSPACE_ROOTuX   워크스페이스 루트 경로 (기본값: $WORKSPACE_ROOT 또는 /home/jay/workspace))typedefaulthelpz--memory-fileNu<   MEMORY.md 파일 직접 지정 (미지정 시 자동 탐색)z--memory-diru:   memory 디렉토리 경로 (기본값: --workspace/memory)z--stale-daysu    stale 기준 일수 (기본값: )z--outputjsonr   u   출력 형식 (기본값: json))choicesr   r   r   scripts)r"   r
   rY   r   Fr[   )ensure_asciiindentz=== Memory Janitor Report (r   z) ===rp   z[MEMORY.md] r   z: r   u   줄 / r   u   줄 [r   ]r   u   [memory/] 총 r$   u   개 파일, r%   ,z bytesrq   z[Stale Files] u   개:   z  - r-   z (rH   u   일 전)rd   z[Duplicates] u   쌍:r]   z, r+   z[Recommendations]r   )argparseArgumentParseradd_argumentr   osenvironrs   r   __file__resolver;   intSTALE_DAYS_DEFAULT
parse_argsr|   r"   memory_filer   r   r=   r   r   outputprintr   dumpsr   rt   )parserargsr|   r"   r
   rY   scripts_dirreportmdmfsfry   recs                r    mainr   |  sj   $$1tuF
RZZ^^$4c$x.:P:P:R:Y:Y:`:`6abcg	   K	   I	   "/0B/C1E	    .	   DnnI*.//ty8?SJ &*&6&6'	2 Ii'K%$%??	F {{fdjjeA>? 	+F;,?+@FG%&RZL2l+;*<F2g;-uUWX`UaTbbcdeN#r-01"EWBXYZA[[abc- N3vm'<#=">dCD]+BQ/ GRZL2l+;*<HEFG,M#f\&:";!<DABl+BQ/ IS]O2diiG.E-FGHI!"+, 	 CD,	 r2   __main__)r   N)__doc__r   r   r   rU   sysr   r   pathlibr   typingr   r   r   r?   dictr   r!   rD   r   listrP   rX   ro   r{   r   r   r   __name__r/   r2   r    <module>r      s     	 	 
 (  
 
 "
D "
T#s(^ "
J9t 9S#X 9x 6H (4 (s (DQUVY[^V^Q_L` (V-# -# -Qd QT#s(^0D Qh/#s(^/d38n%/ T#s(^$/ 
#Y	/dd td{ : )	//4K/ Dz/ 	/
 
#s(^/dS l zF r2   