
    (<i                        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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 h dZg dZg dZg dZg d	Zd
 ZdedefdZdedefdZdedefdZdedefdZdededefdZ	 	 	 	 d*dedededededefdZdededefdZdededefdZ dededefdZ!dedefd Z"dedefd!Z#d+ded"edefd#Z$ded$edededef
d%Z% G d& d'      Z&d( Z'e(d)k(  r e'        yy),u  
project-map.py - 프로젝트 구조 요약 Markdown 파일 생성 CLI 스크립트

Usage:
    python3 scripts/project-map.py <PROJECT_PATH> --output <OUTPUT_PATH> [--depth 2] [--include-tests]

Incremental:
    python3 scripts/project-map.py <PROJECT_PATH> --output <OUTPUT_PATH>         --incremental --changed-files "file1.ts,file2.tsx" --deleted-files "old.ts"

Rollback:
    python3 scripts/project-map.py <PROJECT_PATH> --output <OUTPUT_PATH> --rollback
    N)datetime)Path>   .git.npm.idea.next.yarn.cache.turbo.vercel.vscode.svelte-kitoutdistbuildcoverage__pycache__node_modules)z\.pyc$z\.DS_Store$zThumbs\.db$z\.env$z\.env\.local$z\.env\.\w+\.local$)	__tests__z\.test\.z\.spec\.)GETPOSTPUTDELETEPATCH)z(^|/)\.env(\.|$)z\.pem$credentialsz\.key$z	\.secret$id_rsa
id_ed25519c                     t        j                  d      } | j                  dd       | j                  ddd	       | j                  d
t        dd       | j                  dddd       | j                  dddd       | j                  ddd       | j                  ddd       | j                  dddd       | j                  dd d       | j	                         S )Nu1   프로젝트 구조 요약 Markdown 파일 생성)descriptionproject_pathu#   프로젝트 루트 경로 (필수))helpz--outputTu   출력 파일 경로 (필수))requiredr!   z--depth   u)   디렉토리 트리 깊이 (기본값: 2))typedefaultr!   z--include-tests
store_trueFu-   테스트 파일도 포함 (기본값: False))actionr%   r!   z--incrementalu)   incremental 업데이트 모드 활성화z--changed-files uJ   변경/생성된 파일 목록 (쉼표 구분, 프로젝트 상대 경로))r%   r!   z--deleted-filesuC   삭제된 파일 목록 (쉼표 구분, 프로젝트 상대 경로)z
--rollbacku!   .bak 파일에서 Markdown 복원z--drive-logu)   Drive 변경 로그 파일 경로 (JSONL))argparseArgumentParseradd_argumentint
parse_args)parsers    D/home/jay/workspace/.worktrees/task-2057-dev2/scripts/project-map.pyr-   r-   <   s,   $$GF -RS

T8WX
Q-X   <	   8	   Y  
 R   0	   8  
     namereturnc                 8    | t         v xs | j                  d      S )u2   디렉토리가 제외 목록에 있는지 확인..)EXCLUDE_DIRS
startswith)r1   s    r/   is_excluded_dirr7   l   s    <74??3#77r0   c                 J    t         D ]  }t        j                  ||       s y y)u2   파일이 제외 패턴에 해당하는지 확인.TF)EXCLUDE_FILE_PATTERNSresearch)r1   patterns     r/   is_excluded_filer=   q   s'    ( 99Wd# r0   rel_pathc                 h    t         D ])  }t        j                  || t        j                        s) y y)u2   파일이 민감 패턴에 해당하는지 확인.TF)SENSITIVE_PATTERNSr:   r;   
IGNORECASE)r>   r<   s     r/   is_sensitive_filerB   y   s-    % 99Wh6 r0   path_strc                 J    t         D ]  }t        j                  ||       s y y)u5   경로가 테스트 파일/디렉토리인지 확인.TF)TEST_PATTERNSr:   r;   )rC   r<   s     r/   is_test_pathrF      s'      99Wh' r0   pathbase_dirc                 ,   t         j                  j                  |      }t         j                  j                  ||       }t         j                  j                  |      }|j	                  |t         j
                  z         s||k7  rt        d|       |S )u0   경로 정규화 + Path Traversal 방지 검증..   경로 검증 실패 (Path Traversal 의심): )osrG   realpathjoinr6   sep
ValueError)rG   rH   	real_base	candidatereal_candidates        r/   validate_pathrS      su      *IY-IWW%%i0N$$Y%78^y=XI$RSSr0   rootprefixdepthcurrent_depthproject_rootc                    ||k\  rg S || }g }	 t        | j                         d       }g }|D ]_  }|j                         rt	        |j
                        r')|j                         rt        |j
                        rO|j                  |       a t        |      D ]  \  }	}|	t        |      dz
  k(  }
|
rdnd}|
rdnd}|j                         rJ|j                  | | |j
                   d	       t        |||z   ||dz   |
      }|j                  |       }|j                         s|j                  | | |j
                           |S # t        $ r	 | dgcY S w xY w)u.   디렉토리 트리를 재귀적으로 구성.c                 V    | j                         | j                  j                         fS N)is_filer1   lower)es    r/   <lambda>zbuild_tree.<locals>.<lambda>   s    		QVV\\^7T r0   keyu   [권한 없음]   u
   └── u
   ├── z    u   │   /)rU   rV   rW   rX   )sortediterdirPermissionErroris_dirr7   r1   r\   r=   append	enumeratelen
build_treeextend)rT   rU   rV   rW   rX   linesentriesvisible_entriesentryiis_last	connector	extension	sub_liness                 r/   rk   rk      su    	E,-TU
 O &<<>uzz*]]_

+u%& o. =5s?+a//$+L	%F8	<<>LLF8I;uzzl!<="	)+a/)I LL#]]_LLF8I;uzzl;<!=$ LA  ,(/*++,s   E E! E!include_testsc                    i }t        j                  dt         j                        }g }t        j                  |       D ]  \  }}}|D cg c]  }t        |      r| c}|dd |D ]z  }	|	j                  d      r|	j                  d      r&t        |	      r2t        |      |	z  }
|
j                  |       }t        |      }|st        |      rj|j                  |
       |  |dd }|D ]H  }	 |j                  dd      }|j                  |      }|rt        |j                  |             }|||<   J |S c c}w # t        t         f$ r Y cw xY w)	u8   *.ts 파일에서 export interface / export type 추출.<^\s*export\s+(?:interface|type)\s+([A-Za-z_$][A-Za-z0-9_$]*)N.ts.d.tsi  utf-8replaceencodingerrors)r:   compile	MULTILINErK   walkr7   endswithr=   r   relative_tostrrF   rh   	read_textfindallOSErrorrf   )rX   rv   resultsr<   ts_filesdirpathdirnames	filenamesdfname	full_pathr>   rel_strts_filecontentmatchesrels                    r/   extract_types_interfacesr      s`   GjjG
G H(*(= '$9"*EQ/!2DqE 	'E>>%(ENN7,C&W-I ,,\:H(mG \'%:OOI&	'	'( ~H 	'''KGoog.G'--l;<& N= F6 ) 		s   	D9D90AD>>EEc                    g }t        j                  |       D ]8  \  }}}|D cg c]  }t        |      r| c}|dd |D ]  }|dvr	t        |      |z  }t	        |j                  |             }	|	j                  dd      }
d|
v xs |
j                  d      }d|
v xs |
j                  d      }|s|sv|st        |	      r	 |j                  d	d
      }g }t        D ]M  }t        j                  d| dt        j                        }|j!                  |      s=|j#                  |       O |s|r|
j%                  d      }|dk(  r:|
j%                  d      }d|
|d j                  dd      j                  dd      z   }n|
|t'        d      z   d j                  dd      j                  dd      }n|
j%                  d      }|dk(  r:|
j%                  d      }d|
|d j                  dd      j                  dd      z   }n1|
|t'        d      z   d j                  dd      j                  dd      }|j#                  |||	d        ; |j)                  d        |S c c}w # t        t        f$ r Y ?w xY w)ur   
    app/api/ 또는 pages/api/ 하위 route.ts/route.js 파일에서
    HTTP 메서드와 경로를 추출.
    Nzroute.tszroute.js\rc   	/app/api/app/api//pages/api/
pages/api/r{   r|   r}   /^\s*export\s+(?:async\s+)?(?:function|const)\s+\b	/route.tsr(   	/route.js/app/pages)methodsurlfilec                     | d   S )Nr    )rs    r/   r_   z$extract_api_routes.<locals>.<lambda>>  s
    ah r0   r`   )rK   r   r7   r   r   r   r|   r6   rF   r   r   rf   HTTP_METHODSr:   r   r   r;   rh   findrj   sort)rX   rv   routesr   r   r   r   r   r   r>   
normalizedis_app_routeris_pages_routerr   r   methodr<   idxurl_paths                      r/   extract_api_routesr      s   
 F(*(= >$9"*EQ/!2DqE ;	E44W-I900>?H "))$4J':5Z9N9Nz9ZM+z9`Z=R=RS_=`O!_ \(%;#--wy-Q G& +**FvhbQLL >>'*NN6*+   ook2"9$//*5C"Z%5%=%=k2%N%V%VWbdf%ggH)#F*;*<=EEkSUV^^_jlnoH !oom4"9$//,7C"Z%5%=%=k2%N%V%VWbdf%ggH)#H*=*>?GGUWX``alnpqHMM"  o;	>B KK&K'MC F. _- s   I)I)I..J Jc                     g }t        j                  dt         j                        }t        j                  dt         j                        }t        j                  |       D ]l  \  }}}|D cg c]  }t        |      r| c}|dd t        t        |      j                  |             j                  dd      }	d|	j                  d      v }
|
sp|D ]  }|j                  d      st        |      r!t        |      |z  }t        |j                  |             }|st        |      rW	 |j                  dd	
      }d}|j#                  |      }|r|j%                  d      }n$|j#                  |      }|r|j%                  d      }|s*t        |      j&                  }|d   j)                         r|}|s|j+                  ||d        o |j-                  d        |S c c}w # t        t         f$ r Y *w xY w)uK   components/ 하위 .tsx 파일에서 export default 컴포넌트명 추출.F^\s*export\s+default\s+(?:function|class)\s+([A-Za-z_$][A-Za-z0-9_$]*):^\s*export\s+default\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*[;,\n]Nr   rc   
components.tsxr{   r|   r}   rb   r   )r1   r   c                 (    | d   j                         S )Nr1   )r]   )cs    r/   r_   z$extract_components.<locals>.<lambda>}  s    !F)//"3 r0   r`   )r:   r   r   rK   r   r7   r   r   r   r|   splitr   r=   rF   r   r   rf   r;   groupstemisupperrh   r   )rX   rv   r   pattern_funcpattern_varr   r   r   r   rel_dirin_componentsr   r   r>   r   r1   mr   s                     r/   extract_componentsr   B  s   J ::Q
L **E
K
 )+(= *D$9"*EQ/!2DqE d7m//=>FFtSQ$c(:: !	DE>>&)&W-I900>?H \(%;#--wy-Q D##G,Awwqz&&w/771:DE{''7??$D!!4"BCC!	D*DX OO3O4Y F, _- s   ,G%=G%*G**G=<G=c                    g }t        | j                  d            t        | j                  d            z   }|D ]  }	 t        j                  |j	                  d            }t        |j                  |             }||j                  dd      |j                  dd      t        |j                  di       j                               t        |j                  d	i       j                               t        |j                  d
i       j                               d}|j                  |        |S # t
        t        j                  t        f$ r Y w xY w)uD   package.json 파일 요약 (name, version, dependencies 키 목록).package.jsonz*/package.jsonr{   r~   r1   	(unknown)versiondependenciesdevDependenciesscripts)r   r1   r   r   r   r   )listglobjsonloadsr   r   JSONDecodeErrorrf   r   r   getrd   keysrh   )rX   r   
candidatespkg_pathdatar   infos          r/   summarize_package_jsonr     s3   G l''784*+< J  	::h00'0BCD (&&|45HHV[1xx	;7"488NB#?#D#D#FG%dhh/@"&E&J&J&LMDHHY388:;
 	t" N --? 		s   %D00EEc                    g }t        | j                  d            t        | j                  d            z   }g d}|D ]  }	 |j                  d      }t        j                  dd|t        j
                        }t        j                  d	d|t        j                        }t        j                  |      }t        |j                  |             }|j                  d
i       }|i d}	|D ]  }
|
|v s||
   |	d
   |
<    d|v r|d   |	d<   |j                  |	        |S # t        t        j                  t        f$ r Y w xY w)u3   tsconfig.json 파일 요약 (주요 설정 추출).tsconfig.jsonz*/tsconfig.jsontargetmodulemoduleResolutionlibstrictbaseUrloutDirrootDirjsxesModuleInteropallowSyntheticDefaultImportsresolveJsonModuleincrementalnoEmitr{   r   //.*?$r(   flags	/\*.*?\*/compilerOptions)r   r   paths)r   r   r   r:   subr   DOTALLr   r   r   r   rf   r   r   r   rh   )rX   r   r   
KEY_FIELDSts_pathrawr   r   compilersummaryfields              r/   summarize_tsconfigr     sP   Gl''89D+,= J+J
   		##W#5C&&B2<<@C&&r3bii@C::c?D '%%l3488-r226 	DE 4<UO)*51	D h'0GGw/ 2 N! --? 		s   A5D  EEtop_nc                 :   	 t        j                  g dt        |       ddd      }|j                  dk(  r|j                  j                         ri }d}|j                  j                         D ]  }|j                         }|st        j                  d|      r|}/||vrj|rhd}|j                  d	d
      j                  d
      }|D ]  }t        |      sd} n |s-t        t        j                  j                  |            s|||<   t!        |      |k\  s n t#        |j%                               d| D 	
cg c]
  \  }	}
|	|
d }}	}
|r|S g }t        j,                  |       D ]  \  }}}|D 
cg c]  }
t        |
      r|
 c}
|dd |D ]d  }t        |      rt/        |      |z  }	 |j1                         j2                  }t        |j5                  |             }|j7                  ||f       f  |j;                  d       g }|d| D ]=  \  }}t=        j>                  |      jA                  d      }|j7                  ||d       ? |S c c}
}	w # t         j&                  t(        t*        f$ r Y *w xY wc c}
w # t*        t8        f$ r Y w xY w)uA   최근 수정된 파일 목록 (git log 기반, fallback: mtime).)gitlogz--name-onlyz--pretty=format:%adz--date=shortz-200T   )cwdcapture_outputtexttimeoutr   Nz^\d{4}-\d{2}-\d{2}$Fr   rc   )r   date)reversez%Y-%m-%d)!
subprocessrunr   
returncodestdoutstrip
splitlinesr:   matchr|   r   r7   r=   rK   rG   basenamerj   r   itemsTimeoutExpiredFileNotFoundErrorr   r   r   statst_mtimer   rh   rf   r   r   fromtimestampstrftime)rX   r   resultseencurrent_datelineskippartspartfr   filesfile_mtimesr   r   r   r   r   mtimer   r   date_strs                         r/   get_recently_modified_filesr!    s   * L!
 !fmm&9&9&;DL002 zz|882D9#'L4'L$ $T3 7 = =c B$) &D.t4'+ %&  $,<RWW=M=Md=S,T)5DJ4yE)'* 9=TZZ\8J6E8RS1a+SES
 K(*(= $9"*EQ/!2DqE 		E&W-I!(11)//=>""E3<0		 T"G!&5) 8
s))%099*EX678 N5 T %%'8'B  F _- sP   CI AI "I >II 6JJ1AJI I?>I?JJoutput_pathc                 B   | j                   }t        j                         j                  d      }g }|j	                  d|        |j	                  d|        |j	                  d|         |j	                  d       |j	                  d| d       |j	                  d       |j	                  | d	       t        | || 
      }|j                  |       |j	                  d       |j	                  d       |j	                  d       t        dt        j                         t        | |      }|rQt        |j                               D ]4  \  }	}
dj                  d |
D              }|j	                  d|	 d|        6 n|j	                  d       |j	                  d       |j	                  d       t        dt        j                         t        | |      }|r=|D ]7  }dj                  |d         }|j	                  d| d|d    d|d    d       9 n|j	                  d       |j	                  d       |j	                  d       t        dt        j                         t        | |      }|r&|D ]   }|j	                  d|d    d|d    d       " n|j	                  d       |j	                  d       |j	                  d        t        d!t        j                         t!        |       }|D ]  }|j	                  d"|d           |j	                  d#|d           |j	                  d$|d%           |d&   r&|j	                  d'dj                  |d&                 |d(   r'|j	                  d)dj                  |d(                 n|j	                  d*       |d+   r'|j	                  d,dj                  |d+                 n|j	                  d-       |j	                  d        t#        |       }|D ]  }|j	                  d"|d           |d.   j                         D ]Z  \  }}t%        |t&              r.|j	                  d/| d0dj                  d1 |D                      D|j	                  d/| d0|        \ d2|v rU|j	                  d3       |d2   j                         D ].  \  }}dj                  |      }|j	                  d4| d| d       0 |j	                  d        |j	                  d5       t        d6t        j                         t)        | d78      }|r5t+        |d9      D ]%  \  }}|j	                  | d:|d    d;|d<    d       ' n|j	                  d=       |j	                  d       d>j                  |      S )?u   전체 Markdown 내용 생성.z%Y-%m-%d %H:%M:%S# Project Map: > Generated: > Path: r(   ## Directory Tree (depth: )```rc   rV   rX   ## Types & Interfacesu(   [*] 타입/인터페이스 추출 중...r   , c              3   (   K   | ]
  }d | d   yw`Nr   .0ns     r/   	<genexpr>z$generate_markdown.<locals>.<genexpr>0  s     !:qAaS(!:   - ``: !   _(타입/인터페이스 없음)_## API Routesu   [*] API 라우트 추출 중...r    r      ` → `r   r0     _(API 라우트 없음)_## Componentsu   [*] 컴포넌트 추출 중...r1      _(컴포넌트 없음)_## Configuration Summaryu   [*] 설정 파일 요약 중...### - **Name**: - **Version**: r   r   - **Scripts**: r   - **Dependencies**:    - **Dependencies**: (없음)r   - **DevDependencies**:    - **DevDependencies**: (없음)r   - ****: c              3   2   K   | ]  }t        |        y wr[   r   r2  vs     r/   r4  z$generate_markdown.<locals>.<genexpr>k  s     6K!s1v6K   r   - **paths**:  - `### Recently Modified Files (Top 20)u&   [*] 최근 수정 파일 조회 중...   r   rb   . `` (r     _(파일 없음)_
)r1   r   nowr  rh   rk   rl   printsysstderrr   rd   r  rM   r   r   r   r   
isinstancer   r!  ri   )rX   r"  rV   rv   project_namegenerated_atrm   
tree_lines	types_mapr>   names	names_strr   routemethods_strr   comppkg_summariespkgts_summariestsra   valaliastargetstargets_strrecent_filesrq   items                                r/   generate_markdownrp    sL     $$L<<>**+>?LE 
LL?<.12	LL=/0	LL8L>*+	LL 
LL-eWA67	LL	LLL>#$LLQJ	LL	LL	LL 
LL()	
43::F(}EI%ioo&78 	9OHe		!:E!::ILL3xjI;78	9 	89	LL 
LL!	
+#**=m<F 	TE))E)$45KLL3{m1U5\N'%-PQRS	T 	/0	LL 
LL!	
*<#L-@J 	EDLL3tF|nGDL>CD	E 	./	LL 
LL+,	
+#**= +<8M tCK=)*|CK=12s9~&678y>LL?499S^+D*EFG~LL/		#n:M0N/OPQLL78 !LL2499SAR=S3T2UVWLL:;R" &l3L tBvJ<(),-335 	4HC#t$tC5TYY6Ks6K-K,LMNtC5SE23		4
 b=LL("$W+"3"3"5 Cw"ii0uUG7;-qABC 	R 
LL67	
2D.|2FL q1 	DGAtLLA3c$v,s4<.BC	D 	()	LL99Ur0   c                      e Zd ZdZdZdZ	 d"dedededed	e	f
d
Z
defdZdefdZd Zd#dZde	defdZdede	fdZde	de	fdZde	de	defdZd ZdeddfdZdefdZdede	fdZde	ddfdZdeddfdZded eddfd!Zy)$IncrementalUpdateru[   변경/삭제된 파일만 처리하여 project-map을 증분 업데이트하는 클래스.rb      NrX   r"  rV   rv   drive_log_pathc                     || _         || _        || _        || _        || _        | j                         | _        | j                         | _        d | _	        y r[   )
rX   r"  rV   rv   rt  _get_cache_path
cache_path_get_lock_path	lock_path_lock_fd)selfrX   r"  rV   rv   rt  s         r/   __init__zIncrementalUpdater.__init__  sR     )&
*,..0,,.r0   r2   c                 l    | j                   j                  }d| d}| j                  j                  |z  S )u!   JSON 캐시 파일 경로 생성.z.project-map-cache-z.jsonrX   r1   r"  parent)r{  
project_id
cache_names      r/   rv  z"IncrementalUpdater._get_cache_path  s8    &&++
*:,e<
&&33r0   c                 l    | j                   j                  }d| d}| j                  j                  |z  S )u   Lock 파일 경로 생성.z.project-map-lock-z.lockr~  )r{  r  	lock_names      r/   rx  z!IncrementalUpdater._get_lock_path  s8    &&++
(E:	&&22r0   c                    t        | j                        }t        |d      }t        j                         | j                  z   }	 	 t        j                  |t
        j                  t
        j                  z         || _	        |S # t        $ r[ t        j                         |k\  r,|j                          t        d| j                   d| d      t        j                  d       Y nw xY w)u%   fcntl LOCK_EX 획득 (timeout 30초).wu   Lock 획득 실패: u   초 초과 (r(  g?)r   ry  opentimeLOCK_TIMEOUTfcntlflockLOCK_EXLOCK_NBrz  BlockingIOErrorcloseTimeoutErrorsleep)r{  	lock_filefddeadlines       r/   _acquire_lockz IncrementalUpdater._acquire_lock  s    '	)S!99;!2!22
 B => "	"  99;(*HHJ&.t/@/@.Ai[XYZ  

3  s   =B A!C'&C'c                    |y	 t        j                  |t         j                         	 |j	                          d| _        y# t        $ r Y $w xY w# t        $ r
 Y d| _        yw xY w# 	 |j	                          w # t        $ r Y w w xY wxY w)u   fcntl flock 해제.N)r  r  LOCK_UNr   r  rz  )r{  lock_fds     r/   _release_lockz IncrementalUpdater._release_lock  s    ?	KK/   		
   sX   $A A 	AA' AA' 	A$#A$'B	)A:9B	:	BB	BB	r>   c                    t         j                  j                  |      rt        d|      t	        | j
                  j                               }t         j                  j                  t         j                  j                  ||            }|j                  |t         j                  z         s||k(  st        d|      t        |      rt        d|      t        |      S )u   
        경로 검증 (Path Traversal 방지).
        프로젝트 루트 내 경로인지 os.path.realpath로 확인.
        u,   절대 경로는 허용되지 않습니다: rJ   u-   민감 파일은 처리할 수 없습니다: )rK   rG   isabsrO   r   rX   resolverL   rM   r6   rN   rB   r   )r{  r>   	real_rootrR   s       r/   _validate_pathz!IncrementalUpdater._validate_path  s     77=="KH<XYY))1134	))"'',,y(*KL %%i"&&&89*@M  X&LXLYZZN##r0   	file_pathc                 .   t        j                         }	 t        |d      5 t        fdd      D ]  }|j	                  |        	 ddd       |j                         S # 1 sw Y   xY w# t
        t        f$ r}t        d| d|       |d}~ww xY w)u   SHA-256 해시 계산.rbc                  &     j                  d      S )Ni   )read)r  s   r/   r_   z2IncrementalUpdater._compute_hash.<locals>.<lambda>  s    !&&- r0   r0   Nu   해시 계산 실패: : )hashlibsha256r  iterupdater   rf   	hexdigest)r{  r  hchunkr^   r  s        @r/   _compute_hashz IncrementalUpdater._compute_hash  s    NN	Li& $!!"7= $EHHUO$$
 {{}$ $ ) 	L29+RsCD!K	Ls.   A. &A"
A. "A+'A. .B=BBc                 x   |j                  dd      }t        j                  j                  |      }|dv ry|dv r3d|v xs |j	                  d      }d|v xs |j	                  d	      }|s|ry
|j                  d      r|j                  d      }d|dd v ry|j                  d      r|j                  d      syy)uz   
        파일을 섹션으로 분류.
        반환값: 'types' | 'routes' | 'components' | 'config' | 'other'
        r   rc   r   r   configr   r   r   r   r   r   r   r   Nr   ry   rz   typesother)r|   rK   rG   r  r6   r   r   )r{  r>   r   r   is_appis_pagesr  s          r/   _classify_filez!IncrementalUpdater._classify_file  s    
 %%dC0
  , 55 ,, J.S*2G2G
2SF$
2Yj6K6KL6YH >>&!$$S)EuSbz)# >>% )@r0   sectionc                 
   | j                   |z  }|dk(  rMt        j                  dt        j                        }	 |j	                  dd      }|j                  |      }d|iS |dk(  r|j                  dd      }d	|v xs |j                  d
      }d|v xs |j                  d      }		 |j	                  dd      }g }
t        D ]M  }t        j                  d| dt        j                        }|j                  |      s=|
j                  |       O |r|j                  d	      }|dk(  r:|j                  d
      }d||d j                  dd      j                  dd      z   }n||t        d      z   d j                  dd      j                  dd      }n|j                  d      }|dk(  r:|j                  d      }d||d j                  dd      j                  dd      z   }n1||t        d      z   d j                  dd      j                  dd      }|
|dS |dk(  rt        j                  dt        j                        }t        j                  dt        j                        }	 |j	                  dd      }d}|j                  |      }|r|j                  d      }n$|j                  |      }|r|j                  d      }|s#|j                   }|r|d   j#                         r|}d|iS |dk(  rt$        j&                  j)                  |      }|dk(  r	 t+        j,                  |j	                  d            }d |j/                  dd!      |j/                  d"d!      t1        |j/                  d#i       j3                               t1        |j/                  d$i       j3                               t5        |j/                  d%i       j3                               d&S |d(k(  rg d)}	 |j	                  d      }t        j8                  d*d|t        j                  +      }t        j8                  d,d|t        j:                  +      }t+        j,                  |      }|j/                  d-i       }|D ci c]  }||v s|||    }}d.|d/}d0|v r|d0   |d0<   |S i S # t        t        f$ r dg icY S w xY w# t        t        f$ r g ddcY S w xY w# t        t        f$ r ddicY S w xY w# t        t*        j6                  t        f$ r d'd icY S w xY wc c}w # t        t*        j6                  t        f$ r d'd.icY S w xY w)1u   
        파일에서 해당 섹션 데이터 추출.
        단일 파일 기준으로 데이터를 추출하여 dict 반환.
        r  rx   r{   r|   r}   r   r   rc   r   r   r   r   r(   r   r   r   r   r   Nr   r   r   r   r   r   r   r1   rb   r   r  r   r   packager   r   r   r   r   )r$   r1   r   r   r   r   r$   r   r   r   r   r   r   tsconfig)r$   r   r   )rX   r:   r   r   r   r   r   rf   r|   r6   r   r;   rh   r   rj   r   r   r   rK   rG   r  r   r   r   rd   r   r   r   r   r   )r{  r>   r  r   r<   r   r   r   r   r   r   r   patr   r   r   r   r1   r   r   r   r   r   r   r   r  cor  s                               r/   _extract_file_dataz%IncrementalUpdater._extract_file_data  sP   
 %%0	gjjOG%#--wy-Q!//'2))  !))$4Jz)NZ-B-B:-N  +Rz/D/D\/R 2#--wy-Q G& +jjFvhbQLL ::g&NN6*+  ook2"9$//*5Cj. b1 b12  #3V#4#56 b1 b1  !oom4"9$//,7Cj. b1 b12  #3X#6#78 b1 b1   'x88$::YL **MK&#--wy-Q D##G,Awwqz&&w/771:D ~~DGOO-DD>! GG$$X.E&/::i&9&97&9&KLD ) $ =#'88I{#C(.txx/K/P/P/R(S+1 HH%6;@@B, $(B(?(D(D(F#G	 	 /)
0#--w-?C&&B2<<HC&&r3biiHC::c?D#xx(92>H2<NQX!Xa[.NBN&0RHF(**27*;w!M 	} _- %}$% _- 2#%b112p _- &~%&@  !5!5G /"I../  O
  !5!5G 0"J//0ss   'R )R8 ,S CS+ +BT 6	T TT R54R58SSS('S(+"TTT "T=<T=c                     | j                   j                         sy	 | j                   j                  d      }t        j                  |      S # t
        t        j                  t        f$ r Y yw xY w)u<   JSON 캐시 로드. 없거나 파싱 실패 시 None 반환.Nr{   r   )rw  existsr   r   r   r   r   rf   )r{  r   s     r/   
load_cachezIncrementalUpdater.load_cache  s^    %%'	//++W+=C::c?"--? 		s   0A A/.A/cachec                 $   t        | j                  j                        }	 t        j                  |d      \  }}	 t        j                  |dd      5 }t        j                  ||dd       d	d	d	       t        j                  |t        | j                               y	# 1 sw Y   3xY w# t        $ r' 	 t        j                  |        # t        $ r Y  w xY ww xY w# t        t        f$ r!}t        d
| j                   d|       |d	}~ww xY w)u:   JSON 캐시 저장 (atomic write: tempfile → os.rename)..tmpdirsuffixr  r{   r   Fr#   )ensure_asciiindentNu   캐시 저장 실패: r  )r   rw  r  tempfilemkstemprK   fdopenr   dumprename	Exceptionunlinkr   rf   )r{  r  	cache_dirr  tmp_pathr  r^   s          r/   
save_cachezIncrementalUpdater.save_cache  s   ../		R#++	&ILB
YYr39 FQIIeQU1EF		(C$89F F  IIh'   	 ) 	R24??2C2aSIJPQQ	Rsd   C B, B .1B,  B)%B, ,	C6CC	CCCCC D.D

Dc                 ~   t        dt        j                         t        j                         j                  d      }| j                  j                  }| dgt        | j                  | j                  | j                        z   }i }t        | j                  | j                        }|j                         D ]<  \  }}t        |      r	 | j                  | j                  |z        }|dd|i|d	||<   > t!        | j                  | j                        }	|	D ]E  }
|
d
   }t        |      r	 | j                  | j                  |z        }|d|
d   |
d   d|d	||<   G t#        | j                  | j                        }|D ]A  }|d
   }t        |      r	 | j                  | j                  |z        }|dd|d   i|d	||<   C t%        | j                        }t'        | j                        }||d}t)        | j                  d      }| j*                  ||t-        | j                        | j                  | j                  ||||d
}| j/                  |       t        d| j0                   t        j                         |S # t        $ r d}Y w xY w# t        $ r d}Y Zw xY w# t        $ r d}Y w xY w)uR   전체 스캔하여 JSON 캐시 구조 생성. 기존 extract 함수들 재활용.u&   [*] Full scan → 캐시 생성 중...r,  secondstimespecrc   r*  r(   r  hashr  r   
updated_atr   r   r   r   r  r   r1   packagesr  rR  rS  )
r   r^  r]  r    rV   rv   r  r_  r  rn  u   [*] 캐시 저장 완료: )rY  rZ  r[  r   rX  	isoformatrX   r1   rk   rV   r   rv   r  rB   r  r   r   r   r   r   r!  CACHE_VERSIONr   r  rw  )r{  rX  r]  r_  r  r`  r>   
type_names	file_hashr   rc  r   re  pkg_listts_listr  rn  r  s                     r/   full_scan_to_cachez%IncrementalUpdater.full_scan_to_cache  s   6SZZHlln&&	&:((-- &a()JTZZd>O>O-
 


  -T->->@R@RS	$-OO$5 	 Hj * ..t/@/@8/KL	 "" *-!	E(O	 $D$5$5t7I7IJ 	EV}H * ..t/@/@8/KL	 "#$))$4U5\J!	E(O	  ((9(94;M;MN
 	DF|H * ..t/@/@8/KL	 "'f.!	E(O	  *$*;*;<$T%6%67&G< 343D3DBO ))( 1 12ZZ!//$(
 	*4??*;<3::NE  	"  	"  	s6   J	1JJ-	JJJ*)J*-J<;J<c                    |j                  d| j                  j                        }|j                  dt        j                         j                               }|j                  dt        | j                              }|j                  d| j                        }g }|j                  d|        |j                  d|        |j                  d|        |j                  d       |j                  d	| d
       |j                  d       |j                  dg       D ]  }|j                  |        |j                  d       |j                  d       |j                  d       |j                  di       }t        |j                         D 	
cg c]  \  }	}
|
j                  d      dk(  s|	|
f  c}
}	d       }|r_|D ]Y  \  }}
|
j                  di       j                  dg       }|s+dj                  d |D              }|j                  d| d|        [ n|j                  d       |j                  d       |j                  d       t        |j                         D 	
cg c]  \  }	}
|
j                  d      dk(  s|	|
f  c}
}	d       }|rn|D ]h  \  }}
|
j                  di       }dj                  |j                  dg             }|j                  dd      }|sN|j                  d| d| d| d        j n|j                  d!       |j                  d       |j                  d"       t        |j                         D 	
cg c]  \  }	}
|
j                  d      d#k(  r|	|
f c}
}	d$       }|rG|D ]A  \  }}
|
j                  di       j                  d%      }|s*|j                  d| d| d        C n|j                  d&       |j                  d       |j                  d'       |j                  d(i       }|j                  d)g       D ]I  }|j                  d*|j                  d+d              |j                  d,|j                  d%d-              |j                  d.|j                  d/d-              |j                  d0g       }|r#|j                  d1dj                  |              |j                  d2g       }|r$|j                  d3dj                  |              n|j                  d4       |j                  d5g       }|r$|j                  d6dj                  |              n|j                  d7       |j                  d       L |j                  d8g       D ]  }|j                  d*|j                  d+d              |j                  d9i       j                         D ]Z  \  }}t        |t              r.|j                  d:| d;dj                  d< |D                      D|j                  d:| d;|        \ d=|v rU|j                  d>       |d=   j                         D ].  \  }}dj                  |      }|j                  d?| d| d        0 |j                  d        |j                  d@       |j                  dAg       } | r5t        | dB      D ]%  \  }!}"|j                  |! dC|"d+    dD|"dE    d
       ' n|j                  dF       |j                  d       |j                  dGg       }#|#r|j                  dH       |#D ]\  }$|$j                  dId      }|r|dJdK nd-}%|$j                  dLd      }&|$j                  dMdN      }'|j                  dO|% dP|& dQ|' d
       ^ |j                  d       dRj                  |      S c c}
}	w c c}
}	w c c}
}	w )Su#   JSON 캐시 → Markdown 렌더링.r]  r^  r    rV   r$  r%  r&  r(   r'  r(  r)  r_  r+  r  r  r  c                     | d   S )Nr   r   xs    r/   r_   z4IncrementalUpdater.render_markdown.<locals>.<lambda>C  s
    !A$ r0   r`   r   r-  c              3   (   K   | ]
  }d | d   ywr/  r   r1  s     r/   r4  z5IncrementalUpdater.render_markdown.<locals>.<genexpr>I  s     )GqAaS()Gr5  r6  r7  r8  r9  r   c                 L    | d   j                  di       j                  dd      S )Nrb   r   r   r(   r   r  s    r/   r_   z4IncrementalUpdater.render_markdown.<locals>.<lambda>S  s"    !A$((62.225"= r0   r   r   r:  r;  r0  r<  r=  r   c                 n    | d   j                  di       j                  d      xs dj                         S )Nrb   r   r1   r(   )r   r]   r  s    r/   r_   z4IncrementalUpdater.render_markdown.<locals>.<lambda>h  s.    1Q488FB/33F;ArHHJ r0   r1   r>  r?  r  r  r@  r   rA  r   rB  r   r   rC  r   rD  rE  r   rF  rG  r  r   rH  rI  c              3   2   K   | ]  }t        |        y wr[   rK  rL  s     r/   r4  z5IncrementalUpdater.render_markdown.<locals>.<genexpr>  s     :Oa3q6:OrN  r   rO  rP  rQ  rn  rb   rT  rU  r  rV  drive_changesz## Drive Changes (Recent)	timestampN
   rG   r'   uploadz- r  z (rW  )r   rX   r1   r   rX  r  r   rV   rh   rd   r  rM   r\  r   ri   )(r{  r  r]  r^  r    rV   rm   tlr  rpr  types_itemsr>   r  rb  route_itemsr   rd  r   
comp_itemsr1   r  rg  r   depsdev_depsri  ra   rj  rk  rl  rm  rn  rq   ro  r  change	date_partrG   r'   s(                                           r/   render_markdownz"IncrementalUpdater.render_markdown'  s=   yy1B1B1G1GHyy1I1I1KLyyT5F5F1GH		'4::. 	|n56}\N34x~./R 	1%:;U))L"- 	BLL	UR 	,-		'2&$)KKMR&"bRVVI5F'5Qb"XR
  + A"VVFB/33GR@
 $		)GJ)G GILL3xjI;!?@	A LL<=R 	_%$)KKMS&"bRVVI5F(5Rb"XS=
  + N"vvfb)"iiB(?@hhub)LL3{m1SE
!!LMN LL34R 	_% $kkmB66)$4 R
 K

  * A"vvfb)--f5LL3tfGH:Q!?@A
 LL23R 	/08R(::j"- 	CLL4 3456LL<(D'EFGLL?3779k+J*KLMggi,Gtyy/A.BCD77>2.D3DIIdO3DEF;<ww0"5H6tyy7J6KLM>?LL#	& **Z, 	BLL4vr 2345FF#4b9??A 8Sc4(LL4uD:O3:O1O0P!QRLL4uD!67	8
 "}^,&(k&7&7&9 GNE7"&))G"4KLL5w{m1!EFG LL	 	:;yy4$\15 H4s#d6l^3tF|nAFGH LL,-R 		/26LL45' BZZR0')BsG{	zz&"-Hh7r)BtfBvha@AB LLyyY S  T"s   ^9
1^9
.^?
^?
"_
r   c                 F   t        | j                        }|dz   }t        | j                  j                        }| j                  j                         r	 ddl}|j                  ||       t        j                  |d      \  }}	 t        j                  |dd      5 }|j                  |       ddd       t        j                  ||       y# t        t        f$ r Y uw xY w# 1 sw Y   5xY w# t        $ r' 	 t        j                  |        # t        $ r Y  w xY ww xY w)	uN   Markdown 파일을 atomic write로 저장. 기존 파일은 .bak으로 백업..bakr   Nr  r  r  r{   r   )r   r"  r  r  shutilcopy2r   rf   r  r  rK   r  writer  r  r  )	r{  r   out_strbak_strout_dirr  r  r  r  s	            r/   _write_markdown_atomicz)IncrementalUpdater._write_markdown_atomic  s   d&&'F"d&&--. ""$Wg.
  ''GFCH		2sW5 ! !IIh( _- ! !  			(#   	sZ   C C0 C$0C0 C! C!$C-)C0 0	D :DD 	DD DD c                 &   ddl }t        | j                        }|j                         st	        d| t
        j                         yg }	 t        t        |      dd      5 }|D ]J  }|j                         }|s	 |j                  |      }|j                  dd	      s|j                  |       L 	 ddd       |st	        dt
        j                         yt	        dt        |       dt
        j                         |j                  dg       }	|	j!                  |       |	j#                  d d       |	dd |d<   dj%                  d |D              }
|
rt        t&              j(                  dz  }|j                         r	 t+        j,                  t
        j.                  t        |      dt        |j(                        dd|d   j                  dd      d|
g	ddd      }|j0                  dk(  r)t	        dt        |       dt
        j                         n7t	        d |j                  j                          t
        j                         yyt	        d"| t
        j                         yy# |j                  $ r Y $w xY w# 1 sw Y   xY w# t        $ r(}t	        d
| t
        j                         Y d}~yd}~ww xY w# t        t*        j2                  f$ r(}t	        d!| t
        j                         Y d}~yd}~ww xY w)#u   
        Drive 변경 로그 파일(JSONL)에서 미처리 변경사항을 읽어 캐시에 반영하고,
        drive-change-log.py mark-processed를 호출하여 처리 완료 마킹.
        r   Nu0   [경고] Drive 로그 파일 없음, 건너뜀: r,  r   r{   r   	processedFu%   [경고] Drive 로그 읽기 실패: u)   [*] Drive 로그: 미처리 변경 없음   [*] Drive 로그: u   건 미처리 변경 발견r  c                 &    | j                  dd      S )Nr  r(   r  r  s    r/   r_   z7IncrementalUpdater._process_drive_log.<locals>.<lambda>  s    AEE+r,B r0   T)ra   r  2   ,c              3   J   K   | ]  }|j                  d       s|d      yw)idNr  )r2  r^   s     r/   r4  z8IncrementalUpdater._process_drive_log.<locals>.<genexpr>  s     K1quuT{qwKs   #
#zdrive-change-log.pyz	--log-dirzmark-processedz	--projectprojectunknownz--idsr   )r  r  r  u(   [*] Drive 로그 mark-processed 완료 (u   건)u-   [경고] Drive 로그 mark-processed 실패: u,   [경고] drive-change-log.py 호출 실패: u2   [경고] drive-change-log.py 스크립트 없음: )r   r   rt  r  rY  rZ  r[  r  r   r
  r   r   rh   r   r   rj   rl   r   rM   __file__r  r  r  
executabler  r  )r{  r  _jsonrt  unprocessedr  r  rp   r^   existing_changesids_to_markdrive_log_scriptr  s                r/   _process_drive_logz%IncrementalUpdater._process_drive_log  s   
 	d112$$&B>BRSZZ  	c.)3A 
!Q 	!D::<D ! %D 1$yye<'..u5	!
! =CJJO [!1 22MN	
 !99_b9,"BDQ!1#2!6o hhKKK#H~447LL&&(_'^^NN 01' 5 56,''N..y)D#'
 (,! "F  ((A-Fs;GWFXX\]!$
 KFMML_L_LaKbc!$ HIYHZ[C 1 !00 ! !
! 
!  	9!=CJJO	h  !:!:; _HLSVS]S]^^_sm   J #J=4I81J4J 	CK 8JJ
JJJJ 	K$KKL(LLchanged_filesdeleted_filesc                 p   d}	 | j                         }| j                         }|+t        dt        j                         | j                         }t        j                         j                  d      }|j                  di       }|D ]@  }|j                         }|s	 | j                  |      }|j                         st        d| t        j                         X| j                  st        |      rpt         j"                  j%                  |      }
t'        |
      r	 | j)                  |      }|j-                  |i       }|j-                  d
      |k(  rt        d| t        j                         | j/                  |      }| j1                  ||      }||||d||<   t        d| d| dt        j                         C |D ];  }|j                         }|s||v s||= t        d| t        j                         = t3        d |D              }t3        d |D              }t5        d ||z  D              }|rIt7        | j8                        t;        | j8                        d|d<   t        dt        j                         |j-                  d| j8                  j<                        }| dgt?        | j8                  | j@                  | j8                        z   |d<   tC        | j8                  d      |d<   | jD                  r| jG                  |       |j-                  d| jH                        dz   |d<   ||d <   | jK                  |       | jM                  |      }| jO                  |       t        d!| jP                   t        j                         | jS                  |       y# t        $ r)}	t        d|	 t        j                         Y d}	~	\d}	~	ww xY w# t*        $ r)}	t        d	|	 t        j                         Y d}	~	d}	~	ww xY w# | jS                  |       w xY w)"uz  
        변경/삭제 파일 기반 incremental 업데이트 메인 메서드.

        1. Lock 획득
        2. 캐시 로드 (없으면 full scan)
        3. changed_files 처리
        4. deleted_files 처리
        5. 트리/recent_files 재생성
        6. 캐시 version 증가 후 저장
        7. Markdown 렌더링 후 atomic write
        8. Lock 해제
        Nu&   [*] 캐시 없음 → full scan 수행r,  r  r  r  u*   [경고] 경로 검증 실패, 건너뜀: u#   [경고] 파일 없음, 건너뜀: u*   [경고] 해시 계산 실패, 건너뜀: r  u,   [*] 변경 없음 (hash 동일), 건너뜀: r  u   [*] 업데이트: z
 (section=r(  u   [*] 삭제: c              3   <   K   | ]  }|j                           y wr[   r
  r2  r  s     r/   r4  z,IncrementalUpdater.update.<locals>.<genexpr>       ?Aaggi?   c              3   <   K   | ]  }|j                           y wr[   r  r  s     r/   r4  z,IncrementalUpdater.update.<locals>.<genexpr>  r  r  c              3   ^   K   | ]%  }t         j                  j                  |      d v  ' yw)r  N)rK   rG   r  )r2  ps     r/   r4  z,IncrementalUpdater.update.<locals>.<genexpr>  s-      !   #'HH!s   +-r  r  u   [*] config 재생성 완료r]  rc   r*  r_  rR  rS  rn  r   rb   r^  u    [완료] Markdown 업데이트: )*r  r  rY  rZ  r[  r  r   rX  r  
setdefaultr
  r  rO   r  rv   rF   rK   rG   r  r=   r  r   r   r  r  setanyr   rX   r   r1   rk   rV   r!  rt  r  r  r  r  r   r"  r  )r{  r  r  r  r  rX  r  r>   abs_pathr^   r   new_hashexistingr  r   changed_setdeleted_setconfig_changedr]  
md_contents                       r/   r  zIncrementalUpdater.update*  s    |	(((*G OO%E}>SZZP//1,,.**I*>C$$Wb1E * :\#>>+#228<H  (=hZH ZZ  ))l8.D ((2#E*#11(;H !99Xr2<<'83FxjQ ZZ  --h7 ..xA %& "%	#h *8*JwiqIPSPZPZ[u:\z * F#>>+u$hL
3#**EF ???K???K  !${2! N  6t7H7H I 243D3D E#h 3#**E !99^T5F5F5K5KLL&2^1#5"6!!$BSBS: #E,
 %@!!%E.!
 ""''.  %yyD4F4FG!KE)$'E.! OOE" --e4J''
34T5E5E4FGcjjY w'O " FqcJQTQ[Q[\0  FqcJQTQ[Q[\^ w'si   BP" !N82A2P" %O-6B3P" *F<P" 8	O*O%P" %O**P" -	P6PP" PP" "P5r[   )r2   N)__name__
__module____qualname____doc__r  r  r   r,   boolr   r|  rv  rx  r  r  r  r  r  dictr  r  r  r  r  r   r  r   r  r   r0   r/   rr  rr    sM   eML #  	
  &4 43 3 $"$s $t $6	t 	 	s s BN3 N N NdR R R*WD WvG T G c G Vc d :W W WvJ(D J( J($ J(r0   rr  c                  D   t               } t        | j                        j                         }t        | j                        j                         }|j                         s3t        d| t        j                         t        j                  d       |j                         s3t        d| t        j                         t        j                  d       |j                  j                  dd       | j                  rt        t        |      dz         }|j                         s3t        d| t        j                         t        j                  d       	 d	d l}|j!                  t        |      t        |             t        d
| d| t        j                         y | j&                  rd| j(                  r:| j(                  j+                  d      D cg c]  }|j-                         s| c}ng }| j.                  r:| j.                  j+                  d      D cg c]  }|j-                         s| c}ng }t        d| t        j                         t        d| t        j                         t        dt1        |       dt1        |       t        j                         t3        | dd       }	|	rt        d|	 t        j                         t5        ||| j6                  | j8                  |	      }
	 |
j;                  ||       y t        d| t        j                         t        d| t        j                         t        d| j6                   d| j8                   t        j                         t        dt        j                         t?        ||| j6                  | j8                        }	 |jA                  |d       t        d| t        j                         	 t5        ||| j6                  | j8                        }
|
jC                          y # t"        t$        f$ r=}t        d| t        j                         t        j                  d       Y d }~y d }~ww xY wc c}w c c}w # t<        $ r=}t        d| t        j                         t        j                  d       Y d }~y d }~wt"        t$        f$ r=}t        d| t        j                         t        j                  d       Y d }~y d }~ww xY w# t"        t$        f$ r>}t        d| t        j                         t        j                  d       Y d }~id }~ww xY w# t"        t$        f$ r(}t        d| t        j                         Y d }~y d }~ww xY w)Nu;   [오류] 프로젝트 경로가 존재하지 않습니다: r,  rb   u>   [오류] 프로젝트 경로가 디렉토리가 아닙니다: T)parentsexist_okr  u(   [오류] 백업 파일이 없습니다: r   u   [완료] 롤백 완료: u    → u   [오류] 롤백 실패: r  u   [*] 프로젝트: u   [*] 출력: u    [*] incremental 모드: changed=z
, deleted=	drive_logr  )rX   r"  rV   rv   rt  u	   [오류] z[*] depth: z, include-tests: u   [*] Markdown 생성 시작...)rX   r"  rV   rv   r{   r   u   [완료] 파일 생성: u   [오류] 파일 쓰기 실패: u(   [경고] 캐시 생성 실패 (무시): )"r-   r   r    r  outputr  rY  rZ  r[  exitrg   r  mkdirrollbackr   r  r  r   rf   r   r  r   r
  r  rj   getattrrr  rV   rv   r  r  rp  
write_textr  )argsrX   r"  bak_pathr  r^   r  r  r  r1  updaterr   s               r/   mainr;    s   <D))*224Lt{{#++-K  KL>Zadakakl N|n]dgdndno TD9 }}K(612 <XJGcjjYHHQK	LLXK(89,XJeK=IPSPZPZ[ 	  !! **005C1QC 	 !! **005C1QC 	 	"<.1

C[M*<.s=/A.B*SQ^M_L`a	

 D+t4	&yk2D$%#**,,$
	NN=-8 	 
|n
-CJJ?	L
&SZZ8	K

|#4T5G5G4H
IPSPZPZ[	
)

;!jj((	Gw9(6SZZH	O$%#**,,	
 	""$U ) 	,QC0szzBHHQKK	 D
 D2  	IaS/

3HHQKK 	 ) 	IaS/

3HHQKK	* _% /s3#**E _% O8<3::NNOs   A	P, Q;1Q;#R 9R R 1T 83U( ,Q8;3Q33Q8	T3ST3TTU%'3U  U%(V7VV__main__)r(      r   N)rR  ))r+  r)   r  r  r   rK   r:   r  rZ  r  r  r   pathlibr   r5   r9   rE   r   r@   r-   r   r,  r7   r=   rB   rF   rS   r,   r   rk   r-  r   r   r   r   r   r!  rp  rr  r;  r(  r   r0   r/   <module>r?     s       	 	  
     

 9 -`8# 8$ 8
3 4   3 4  s s  1
11 1 	1
 1 
1h+4 + + +\IT I$ I4 IX<T <$ <4 <~ $ :&T &d &RDd D3 D DNsss s 	s
 	stl( l(fgOT zF r0   