
    9j                        U 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	m
Z
 ddlmZ  e e
e      j                         j                   d         Zeej$                  vrej$                  j'                  de       ddlmZ  ej,                  e      Zdeeef   fdZ	 	 	 dAd	ee   d
edz  dededej:                  e   f
dZdBd	ee   d
edz  defdZdedefdZ dedeeeef      fdZ!dededefdZ"dedededefdZ#d Z$dZ%dededee   fdZ&e$	 	 	 	 	 dCdededededededededz  deeef   fd        Z'dede(fd!Z)ded"ed#edefd$Z*d%ed&ed'ed
edeeeef      f
d(Z+	 dDd)eeeef      d*ed+edeeef   fd,Z,g d-Z-ee   e.d.<   g d/Z/ee   e.d0<   dDd)eeeef      d+edeeeef      fd1Z0d2eeeef      ddfd3Z1e$	 	 	 	 dEdededed4ed5ed6ed7e(d+edeeef   fd8       Z2dedeeef   fd9Z3dedeeef   fd:Z4dededeeef   fd;Z5ddd<dedededededz  defd=Z6dejn                  fd>Z8dFd?Z9ed@k(  r e9        yy)Gu  
Git Worktree Manager - 멀티봇 환경에서의 안전한 worktree 관리 스크립트.

사용법:
    python3 worktree_manager.py <command> [args]

명령어:
    create   <project_path> <task_id> <team_id>        - worktree 생성
    finish   <project_path> <task_id> <team_id> <action> - worktree 완료 처리
    list     <project_path>                             - 활성 worktree 목록
    status   <project_path> <task_id>                  - worktree 상태 확인
    N)Path)Any   )parse_blast_radiusreturnc                     t        t              j                  dz  } t        j                  d      }i }| j                         r$	 t        j                  | j                               }|j                  d      |k7  r|dd}|dxx   dz  cc<   | j                  t        j                  |             |d   dkD  rd	d
|d    diS i S # t        j                  t        f$ r i }Y |w xY w)uB   Gemini 무료 한도 (일 33건) 체크. 초과 시 경고 반환.zgemini_rate_tracker.jsonz%Y-%m-%ddater   )r	   countr
   r   !   warningzGemini daily limit exceeded: z/33)r   __file__parenttimestrftimeexistsjsonloads	read_textJSONDecodeErrorOSErrorget
write_textdumps)tracker_pathtodaydatas      scripts/worktree_manager.py_check_rate_limitr   '   s    >((+EELMM*%ED	::l4467D xx5 *MQMDJJt,-G}r:4=/MNNI $$g. 	D	s   #C C%$C%argscwdcheckcapturec                     t        j                  | ||d|rt         j                  nd|rt         j                        S d      S )u1  git 명령을 실행하고 CompletedProcess 반환.

    Args:
        args: 실행할 명령어 리스트.
        cwd: 작업 디렉터리.
        check: 실패 시 CalledProcessError 발생 여부.
        capture: stdout/stderr 캡처 여부.

    Returns:
        완료된 프로세스 객체.
    TN)r    r!   textstdoutstderr)
subprocessrunPIPE)r   r    r!   r"   s       r   _runr*   ?   sD    " >>")zt")z  04     c                 R    t        | |d      }|j                  j                         S )u/   명령 실행 후 stdout을 strip하여 반환.Tr    r!   )r*   r%   strip)r   r    results      r   _outr0   Z   s#    $Ct,F==  r+   project_pathc                    	 t        g d|       }|j                  d      d   S # t        j                  $ r Y nw xY w	 t        g d|       }|j	                         D cg c]  }|j                          nc c}w }}dD ]
  }||v s|c S  n# t        j                  $ r Y nw xY w	 t        g d|       S # t        j                  $ r Y yw xY w)	u   메인 브랜치명 감지.

    git symbolic-ref refs/remotes/origin/HEAD → 실패 시 main/master fallback.

    Args:
        project_path: git 저장소 루트 경로.

    Returns:
        메인 브랜치명 (예: "main" 또는 "master").
    )gitzsymbolic-refzrefs/remotes/origin/HEADr    /)r3   branch--format=%(refname:short))mainmaster)r3   	rev-parsez--abbrev-refHEADr9   )r0   splitr'   CalledProcessError
splitlinesr.   )r1   refbranches_rawbbranches	candidates         r   detect_main_branchrE   `   s    ?

 yy~b!!(( 
:
 (4'>'>'@A!AGGIAAA+ 	!IH$  	! (( @lSS(( sG   "% ;;!B  A87B 	B B B%$B%)B8 8CCc                    t        g d|       }g }i }|j                         D ]L  }|j                         s|r|j                  |       i })d|v r|j	                  d      \  }}}|||<   Hd||<   N |r|j                  |       |S )u   git worktree list --porcelain 파싱.

    Args:
        project_path: git 저장소 루트 경로.

    Returns:
        각 worktree 정보 딕셔너리 리스트.
        키: "worktree", "HEAD", "branch" (branch는 없을 수 있음)
    )r3   worktreelist--porcelainr4     )r0   r?   r.   append	partition)r1   output	worktreescurrentlinekey_values           r   list_worktrees_rawrU      s     2F
 ')I G!!# zz|  )$; NN3/MCE GCL GDM !r+   task_idteam_idc                     d|  d| S )u   worktree 브랜치명 생성.task/- )rV   rW   s     r   branch_namer\      s    7)1WI&&r+   c                 @    t        t        |       dz  | d| z        S )u$   worktree 디렉터리 경로 생성.z
.worktreesrZ   )strr   )r1   rV   rW   s      r   worktree_pathr_      s'    tL!L0gYay3IIJJr+   c                 v     t        j                         dt        dt        t        t        f   f fd       }|S )Nr1   r   c                 .   |j                  dd      r 	| g|i |S t        |       dz  }|j                         sdd|  dS t        |       dz  }d}t        j                  g d| d	
      }|j
                  dk7  rgt        |d      5 }|j                         r/|j                         j                  dkD  r|j                  d       n|j                  d       d d d        d	} 	| g|i |}t        |t              r|rd	|d<   |S # 1 sw Y   0xY w)N	read_onlyFz.giterrorzNot a git repository: statusmessagez
.gitignore)r3   zcheck-ignorez-qz.worktrees/T)r    capture_outputr   az
.worktrees/
z.worktrees/
gitignore_updated)r   r   r   r'   r(   
returncodeopenstatst_sizewrite
isinstancedict)
r1   r   kwargsgit_dirgitignore_pathri   check_resultfr/   funcs
            r   wrapperz)validate_worktree_safety.<locals>.wrapper   s%    ::k5)6t6v66|$v-~~%4J<.2YZZl+l:!!~~8
 ""a'nc* -a!((*~/B/B/D/L/Lq/PGG-.GGO,	-
 !%l4T4V4fd#(9*.F&'- -s   ADD)	functoolswrapsr^   rp   r   )rv   rw   s   ` r   validate_worktree_safetyrz      s<    __Tc tCH~  @ Nr+   )z.envz
.env.localz.env.developmentz.env.development.local	dest_pathc                 x   ddl }t        |       }t        |      }g }|j                         r|j                         s|S t        D ]A  }||z  }|j	                         s||z  }	 |j                  ||       |j                  |       C |S # t        $ r%}	t        j                  d| d|	        Y d}	~	od}	~	ww xY w)u  project_path 루트의 .env 패턴 파일들을 dest_path로 복사.

    `.env.example`, `.env.sample` 같은 템플릿은 무시한다 (이미 git에 커밋됨).
    secret 안전: 파일 내용/절대경로는 로그/응답에 노출하지 않고 basename만 반환.

    Args:
        project_path: 원본 프로젝트 루트 경로.
        dest_path: 복사 대상 (워크트리) 경로.

    Returns:
        복사된 파일명 리스트 (basename만).
    r   Nz[copy_env] u    복사 실패: )
shutilr   is_dir_ENV_COPY_PATTERNSis_filecopy2rL   r   loggerr   )
r1   r{   r}   src_rootdst_rootcopiednamesrcdstexcs
             r   _copy_env_filesr      s     L!HIHF??HOO$5" o{{}o	LLc"MM$ M	  	NN[.>seDE	s   $#B	B9B44B9origin/mainrb   copy_envbase_refenforce_origin_base
events_dirc           
         |rddddg |dddd	S t        ||      }t        | ||      }	t        |       }
|
D ]#  }|j                  d      |	k(  sd|	|g |ddddc S  t	        |	      j
                  j                  d	d	
       	 t        ddd|g|        d	}d}d}|rt        ddd|	|g|        nP|r"t        g d| d       	 t        ddd|g|       }|rt        dddd||	|g|        nd	}t        dddd||	g|        t        dddd|g|	d       g }|rt        | |	      }d}	 ddl}|rt	        |      nt	        |       dz  dz  }|j                  d	d	
       || dz  }||||	||t        |xr |       |j                  j                  |j                  j                         j#                         d}|j%                  t'        j(                  |dd             t+        |      }d|	||||||dS # t        j                  $ r d}Y uw xY w# t        j                  $ r d}Y Tw xY w# t,        $ r }t.        j1                  d|       Y d}~gd}~ww xY w)ul  worktree 생성 또는 재사용.

    Args:
        project_path: git 저장소 루트 경로.
        task_id: 태스크 식별자.
        team_id: 팀 식별자.
        read_only: True이면 worktree 미생성 (read 에이전트 전용).
        copy_env: True이면 워크트리 생성 직후 project_path 루트의 .env 패턴
            파일을 워크트리로 자동 복사 (task-2377). 기본 True.
        base_ref: worktree 기반으로 삼을 origin 참조 (기본 "origin/main").
            enforce_origin_base=True일 때 신규 브랜치 생성에 사용.
        enforce_origin_base: True이면 신규 브랜치 생성 시 origin/main SHA를
            강제 base로 사용 (stale-base PR 방지, task-2700). 기본 True.
        events_dir: base SHA 마커를 저장할 디렉터리.
            None이면 <project_path>/memory/events 사용.

    Returns:
        {"status": "created"|"reused"|"skipped", "worktree_path": str|None,
         "branch": str|None, "copied_env_files": list[str],
         "base_ref": str, "base_sha": str|None,
         "base_marker_path": str|None, "base_fallback": bool}
    skippedNu#   read-only agent: worktree 미생성F)	re   r_   r7   reasoncopied_env_filesr   base_shabase_marker_pathbase_fallbackrG   reused)re   r_   r7   r   r   r   r   r   Tparentsexist_okr3   r;   --verifyr4   addr3   fetchoriginr-   z-bpush-ur   r   memoryevents.worktree-base.json)rV   rW   r7   r_   r   r   enforced
created_at   ensure_asciiindentuI   [task-2700] base SHA 마커 기록 실패 (worktree 생성은 완료): %screated)r\   r_   rU   r   r   r   mkdirr0   r'   r>   r*   r   datetimeboolnowtimezoneutc	isoformatr   r   r   r^   	Exceptionr   r   )r1   rV   rW   rb   r   r   r   r   r7   wt_pathexistingwtbranch_existsorigin_base_shar   
copied_envr   _dt_events_dir_marker_file_marker_data_es                         r   
cmd_creater     s   B !; "  $"

 
	
 '*FL'7;G ",/H 66*("!( $&$ $(!&	 	 	Mtd;KV4	
 
 #'OMJw7	
 +UK'"&KX>$# 
E4/R  !M
E4A  		h/ J$\7;
 $(h*4d:&$|:Lx:WZb:b$6"y0C%DD$ '_B]1BC,,**3<<+;+;<FFH	
 	

<eTU VW|,
  &#,&	 	W (( 0 00 '"&'`  hbdfgghs=   G2 H $CH* 2H
HH'&H'*	I3IIc                 .   t        t              j                  j                  dz  dz  }t        |j	                  |  d            t        |j	                  |  d            z   }|D ]z  }	 |j                  d      }|j                  d      rT|j                  dd      }|d	kD  r=	 d	d
l}|j                  |d|       }t        |t              rd|v rt        |d         c S | t        t              j                  j                  dz  dz  }
	 t+        j,                  |
j                  d            }|j/                  | i       }|j/                  dd      }|r)t        |j1                  dd      j3                               S 	 y	# t        $ rH d	d
l} |j                  d|d| |j                         }	|	rt        |	j#                  d            cY c S Y w xY w# t$        t&        t(        f$ r Y w xY w# t$        t*        j4                  t&        f$ r Y y	w xY w)u6   task 파일에서 level을 읽어 반환. 없으면 0.r   tasksz.mdz*.mdutf-8encodingz---   r   Nlevelz^level:\s*(\d+)r   ztask-timers.json
work_levelrK   lv)r   r   r   rH   globr   
startswithfindyaml	safe_loadro   rp   intImportErrorresearch	MULTILINEgroupr   
ValueError	TypeErrorr   r   r   replacer.   r   )rV   	tasks_dir
candidatespr$   endr   fmr   mtimers_pathtimersentrywls                 r   _resolve_task_levelr     s   X%%,,x7'AIinny_56innPWyX\M]>^9__J 	;;;0Du%iiq)73#!^^D3K8%b$/GrM#&r'{#33. x.''..9<NNKK1171CD

7B'YY|R(rzz$+11344  + ' 3!%BII&8$q+r||T#&qwwqz?2 	3 Y/ 		 T)):6 sJ   +:G&9FA3G3 A
GGGGGG0/G03HHr7   main_branchc                 2   ddl } |j                  t              }	 t        j                  ddd| d| ddg| d	d
t        j
                  t        j
                  d      }|j                  dk7  r-|j                  d|j                  j                                 y|j                  j                         j                         D cg c]#  }|j                         s|j                         % }}|syd}d|d| dg|z   dgz   }	t        j                  |	| d	d
t        j
                  t        j
                  d      }
|
j                  dk7  r-|j                  d|
j                  j                                 yt        j                  |
j                        }t        |      \  }}dj                  |      }|rdj                  |      nd}|rdj                  |      nd}dt!        |       d| dt!        |       d| dt!        |       d| d}|S c c}w # t        j"                  $ r |j                  d       Y yt        j$                  t&        f$ r}|j                  d|        Y d}~yd}~wt(        $ r}|j                  d|        Y d}~yd}~ww xY w)u8  AST 의존성 분석으로 PR blast radius 요약 생성.

    Args:
        project_path: git 저장소 루트 경로.
        branch: 분석할 브랜치명.
        main_branch: 비교 기준 메인 브랜치명.

    Returns:
        Blast radius 요약 마크다운 문자열. 실패 시 빈 문자열.
    r   Nr3   diffz--name-onlyz...--z*.pyFT   )r    r!   r$   r%   r&   timeoutu    [blast_radius] git diff 실패: rK   z1/home/jay/workspace/scripts/ast_dependency_map.pypython3z--rootz--files--jsonu(   [blast_radius] AST 스크립트 실패: z, u   없음u!   ## Blast Radius
- 변경 파일: u   개 (u   )
- 영향받는 파일: u   )
- 관련 테스트: )u*   [blast_radius] 타임아웃 (30초 초과)u#   [blast_radius] JSON 파싱 실패: u(   [blast_radius] 예상치 못한 오류: )logging	getLogger__name__r'   r(   r)   rj   r   r&   r.   r%   r?   r   r   r   joinlenTimeoutExpiredr   KeyErrorr   )r1   r7   r   r   r   diff_resultru   changed_files
ast_scriptast_cmd
ast_resultr   direct_importers
test_fileschanged_strimporters_str	tests_strsummaryes                      r   _get_blast_radius_summaryr     s    Wx(F< nnFMk]#fX+FfU????
 !!Q&NN=k>P>P>V>V>X=YZ[,7,>,>,D,D,F,Q,Q,SaqWXW^W^W`aa I
j(L)L}\`h_ii^^????

   A%NNEjFWFWF]F]F_E`abzz*++,'9$'?$* ii.7G		"23X-7DIIj)X	  #M 235 F&&)*:&;%<E- Q##&z?"351F 	 G bJ $$ CD  (+ <QC@A A!EFsV   A>H *H HH-H 2BH 6BH H $J;JI,,J8JJ	pr_numberowner	repo_namec                 j   t        ddd| d| d|  dg|d      }|j                  d	k7  rg S 	 t        j                  |j                        }g }|D ]=  }|j                  d
i       j                  dd      }d|j                         vr9|j                  dd      }	d}
|	j                         }t        j                  dt        j                        }t        j                  dt        j                        }d|v s)d|v s%d|	v s!d|	v sd|	v s|j                  |	      sd|v sd|v rd}
n'd|v s!d|	v sd|	v sd|	v s|j                  |	      sd|v rd}
|j                  |
|j                  d d      |	d!d" t        |j                  d#d	            d$       @ |S # t        j
                  t        f$ r g cY S w xY w)%u   GitHub API로 PR 코멘트를 파싱하여 severity 분류된 리스트 반환.

    Returns:
        [{"severity": "high"|"medium"|"low", "path": str, "body": str, "line": int}]
    ghapirepos/r5   /pulls/z	/commentsFr-   r   userloginrK   geminibodylowz&!\[(security-critical|critical|high)\]z!\[(medium)\]zseverity: highzseverity: criticalu   🔴HIGHCRITICALzhigh-priority.svgzcritical.svghighzseverity: mediumu   ⚠️MEDIUMWARNINGzmedium-priority.svgmediumpathNi  rQ   )severityr  r
  rQ   )r*   rj   r   r   r%   r   r   r   lowerr   compile
IGNORECASEr   rL   r^   )r   r   r  r    comments_resultcommentsparsedcommentr  	body_textr  
body_lower_high_img_re_medium_img_res                 r   _parse_gemini_commentsr  0  s    	uugQyk9MNO
 !!Q&	::o445 $&F %
{{62&**7B74::<'KK+	__&
zz"KR]][$4bmmD
*#z1""Y&""9-"j0+H*,9$9$I%$$Y/$
2H$FB/!$3GKK23		
=%
L MU   (+ 	s   F F21F2r  r_   collect_modec                    | D cg c]  }|d   dk(  s| }}|sg dddS g }|D ]'  }d|d    d|d	    d
|d    }|j                  |       ) |r|dddS dj                  |      }t        dd|dg|d       t        g d|d       t        g d|d       t        g d|d      }|j                  dk(  r|j                  j                         nd}	t        ddg|d       |d|	dS c c}w )u	  HIGH severity 코멘트를 기반으로 자동 수정 프롬프트 생성 및 실행.

    collect_mode=False(기본값)이면 실제 수정 실행. True이면 프롬프트만 생성.

    Returns:
        {"prompts": [...], "executed": bool, "diff_stat": str}
    r  r  FrK   )promptsexecuted	diff_statz)Fix the following HIGH severity issue in r  z (line rQ   z):
r
  z

---

claudez-pz--dangerously-skip-permissionsr-   )r3   r   z-A)r3   commitz-mz&[auto-fix] Gemini HIGH severity issues)r3   r   z--statzHEAD~1r   r3   r   T)rL   r   r*   rj   r%   r.   )
r  r_   r   chigh_commentsr"  promptcombined_promptr   r$  s
             r   _auto_fix_high_commentsr+  m  s>    !)D1AjMV,CQDMD5rBBG <QvYK{STU[S\R]]abcdjbkalmv "BGG $((1O	4*JK 		=>I +K
 /:.D.D.I""((*rI 	%m59DyIIO Es
   C+C+)zsql injectionxsscsrfauth
permissionznull pointerzrace conditiondeadlockz	data loss
corruptionzenv variableconfigaccessibilitya11y
responsiveMEDIUM_FIX_PATTERNS)znaming conventionz
code style
formatting
whitespaceztype annotation	docstringMEDIUM_SKIP_PATTERNSc           
      @   | D cg c]  }|d   dk(  s| }}g }|D ]y  }|d   j                         }|rd}d}n3d}d}t        D ]  }||v sd}|} n |dk(  rt        D ]  }||v sd}|} n |j                  d||d   |j	                  d	d      |d
       { |S c c}w )uS  MEDIUM 코멘트를 FIX/SKIP/DEFER 3종으로 자동 분류.

    collect_mode=False(기본값)이면 패턴 매칭으로 분류. True이면 모든 MEDIUM을 DEFER로 분류하고 로그만 수집.

    Returns:
        [{"severity": "medium", "classification": "FIX"|"SKIP"|"DEFER", "body": str, "path": str, "pattern_matched": str}]
    r  r  r
  DEFERr   rK   FIXSKIPr  )r  classificationr
  r  pattern_matched)r  r6  r:  rL   r   )	r  r   r'  medium_comments
classifiedr  r?  r@  patterns	            r   _classify_medium_commentsrD    s     #+HQa
mx.GqHOH')J !
vY__&
$N,O %N O. j(%*N&-O	 (3 G*,)/*1	 	$"0&	fb)#2	
3!
F M Is
   BBrB  c           	      f   t        t              j                         j                  j                  dz  dz  dz  }g }|j	                         r$	 t        j                  |j                               }| D ]W  }t        j                  d      |d   |d   dd |j                  d	d
      |j                  dd
      d}|j                  |       Y |j                  j                  dd       |j                  t        j                   |dd             y# t
        j                  t        f$ r g }Y w xY w)u0   MEDIUM 분류 결과를 로그 파일에 기록.	dashboardr   zmedium-comments-log.jsonz%Y-%m-%dT%H:%M:%Sr?  r
  N   r  rK   r@  )	timestampr?  r
  r  r@  Tr   r   F)r   r   )r   r   resolver   r   r   r   r   r   r   r   r   r   rL   r   r   r   )rB  log_pathr   r'  r   s        r   _log_medium_commentsrK    s   H~%%'..55CfLOiiH%'H	zz("4"4"67H  ':; 01fIdsOEE&"% uu%6;
 	 OO$6

8AEJK $$g. 	H	s   #D D0/D0actionpr_titlepr_bodygemini_timeoutc                 F  9 |dk(  rt        |      }|dk\  rdnd}t        ||      }	t        | ||      9t        |       }
t	        9fd|
D              }|dk(  r[d}|rQt        |       }t        g d9d	       t        d
d|dg9d	      }|j                  dk(  rd}nt        g d9d	       d}d|	|dS |dk(  r|st        d9       t        |       }t        g d9d	       t        d
d|dg9d	      }|j                  dk7  r1t        g d9d	       |j                  xs d}t        d|	 d|       t        d
d|g|        t        d
dd|	g| d	      }|j                  dk7  r"t        g d| d	       t        d|	 d| d      t        d
ddd9g|        t        d
d d!|	g| d	       t        d
d"d#d$|	g| d	       d%|	d&S |d'k(  rl|st        d9       t        g d(9d	       t        g d)9d	       t        d
ddd9g|        t        d
d d*|	g| d	       t        d
d"d#d$|	g| d	       d+|	d&S |dk(  r|st        d9       t        |       }t        g d9d	       t        d
d|dg9d	      }|j                  dk7  rt        g d9d	       t        d|	       t        d
d"d,d#|	g9d	       |xs d-| d.| }t        | |	|      }|r|d/z   |z   }t        j                  d0|xs d1| d2       t               }t        |       d3z  d4z  }|j                         s1dd5l}|j"                  j%                  d6d7      }t        |      d3z  d4z  }t        d8t'        |      d9|d:g9d	      }t        j                  d;|j                         d}d}	 dd5l}dd5l}|j"                  j%                  d6d7      }t        |      d<z  d=z  | d>z  }|j                         rO|j+                  |j-                  d?@            }|j%                  dAi       j%                  dB      }|rt'        |      }|j                  dk7  r|st        dD|j2                         d}!d}"dE}#t        g dF9d	      }$d}%d}&|$j                  dk(  rSt)        j*                  |$j                        }'|'j%                  dGi       j%                  dHd      }%|'j%                  dId      }&|"|k  rm|rkt        dJdKdL|% dM|& dN| dOg9d	      }(|(j                  dk(  rdP|(j                  j5                         v rdQ}!n"t7        j8                  |#       |"|#z  }"|"|k  r|rkd})g }*d}+dR},g }-g }.|!s|rt        j                  dS       t7        j8                  dT       t        dJdKdL|% dM|& dN| dOg9d	      }/|/j                  dk(  r3dP|/j                  j5                         v rdQ}!t        j                  dU       |!rE|rBt;        |,      D ]  }0t=        ||%|&9      }-|-D 1cg c]  }1|1dV   dWk(  s|1 }2}1t?        |2      })|)dk(  s|0|,dXz
  k(  r!|-D 1cg c]  }1|1dV   |1dY   |1dZ   d5d[ d\ }*}1 n|+dXz  }+tA        |29|]      }3|r!|-D 1cg c]  }1|1dV   |1dY   |1dZ   d5d[ d\ }*}1 n|3j%                  d^      sd}4|4|k  st        dJdKdL|% dM|& dN| dOg9d	      }5|5j                  dk(  rdP|5j                  j5                         v rt7        j8                  dE       |4dEz  }4|4|k  rg tC        |-|]      }.|.rtE        |.       d_}6|!sd`}7n
|)dk(  rda}7ndb}7|!sdc}6t        j1                  dd|       n|)dk(  rt        j                  de       df}6ndg}6|6|	|||!|! |7|)|*|+|.r7|.D 1cg c]+  }1|1dh   |1j%                  dYd      |1j%                  did      dj- c}1ng dk}8|r|j%                  dld      |8dm<   |8S tG        dn| do      # t.        $ r!} t        j1                  dC|        Y d5} ~ d5} ~ ww xY wc c}1w c c}1w c c}1w c c}1w )pu  worktree 작업 완료 처리.

    Args:
        project_path: git 저장소 루트 경로.
        task_id: 태스크 식별자.
        team_id: 팀 식별자.
        action: "merge" | "discard" | "keep" | "pr" | "auto" (auto: task level에 따라 자동 결정)
        pr_title: PR 제목 (기본값: "[{task_id}] worktree finish").
        pr_body: PR 본문.
        gemini_timeout: Gemini 리뷰 대기 최대 시간(초).
        collect_mode: False이면 실제 수정 루프 실행(기본값), True이면 프롬프트 수집만.

    Returns:
        {"status": "merged"|"discarded"|"kept"|"pending"|"merge_failed"|"blocked_by_high_severity", "branch": str}
    autor   prkeepc              3   F   K   | ]  }|j                  d       k(    yw)rG   N)r   ).0r   r   s     r   	<genexpr>zcmd_finish.<locals>.<genexpr>3  s     DR266*%0Ds   !r   r   Fr-   r3   mergez	--no-editr   synced)r3   rW  z--abortconflictkept)re   r7   sync_with_mainzworktree not found: rK   uC   main 최신화 중 충돌 발생. 수동 해결 필요.
브랜치: u   
충돌 파일:
checkoutr4   z--no-ffz'Merge conflict detected while merging 'z' into 'z'. Resolve conflicts manually.rG   remove--forcer7   -dr   r   --deletemerged)re   r7   discard)r3   r\  r   .)r3   cleanz-fdz-D	discardedr   zTask z	 by team z

uE   [task-2467] worktree_manager → taskctl pr-open 위임 (pr_title=%r)[z] worktree finishscriptsz
taskctl.pyNWORKSPACE_ROOTz/home/jay/workspacer   zpr-openz--autou2   [task-2467] taskctl pr-open --auto 호출: exit=%sz.tasksstatez.jsonr   r   evidencer   u:   [task-2467] state 파일에서 PR 번호 읽기 실패: %su   taskctl pr-open 실패: r   )r  repoviewr   z
owner,namer   r  r   r  r  r  r5   r  z/reviewszgemini-code-assistTr   u6   [gemini-retry] 타임아웃 후 60초 추가 대기...<   u;   [gemini-retry] 재확인 성공 — Gemini 리뷰 감지됨r  r  r   r  r
  rG  )r  r  r
  )r   r#  pendingTIMEOUTPASSBLOCKEDblocked_by_timeoutzJGemini review timed out for PR #%s. Merge blocked. Manual review required.uU   [task-2467] worktree_manager에서 자동 머지 폐기. taskctl merge 호출 안내.pr_opened_taskctl_pendingblocked_by_high_severityr?  r@  )r?  r  r@  )re   r7   pr_urlr   gemini_reviewedrO  gemini_verdicthigh_severity_countreview_summaryauto_fix_attemptsmedium_classifiedr   rate_limit_warningzUnknown action: 'z'. Use merge|discard|keep|pr.)$r   r\   r_   rU   anyrE   r*   rj   RuntimeErrorr%   r   r   infor   r   r   osenvironr   r^   r   r   r   r   r   r&   r  r   sleepranger  r   r+  rD  rK  r   ):r1   rV   rW   rL  rM  rN  rO  r   r   r7   r   wt_foundsync_statusr   sync_resultconflict_infomerge_resultr
  blast_summaryrate_limit_resulttaskctl_path_os_ws	pr_resultru  r   _json_os2_ws2_state_path_state_data_pr_nr   gemini_foundelapsedinterval	repo_infor   r  rireviews_resultrx  ry  rz  max_auto_fix_attemptsr  r{  retry_resultattemptr'  r(  
fix_result
re_elapsed
re_reviewsmerge_statusrw  r/   r   s:                                                            @r   
cmd_finishr    s	   6 #G,!'*FL'7;G ",/HD8DDH,\:K+Fk:K
 %%*&0gUK( FkRR!5gY?@@(6 	'WEBG[+6

 !!Q&,'G'..4"MZ[aZbbx  zG  yH  I 
 	eZ-<@GY/
 ""a',,eL9& B= >@  	eZ9g>LQeXtV,,eLFHj&9	
 #f55!5gY?@@ 	+F$'?eZ9g>LQeXtV,,eLFHj&9	
 &88~!5gY?@@(6 	'WEBG[+6

 !!Q&,'G!eflemnoo 	eVT8V4'O
 =E')G9=1,T&==0D[]e  ^Hklmtlu  vG  jH  	I-/ L)I5D""$++//"24IJC9y0<?LL)9gxH
	
 	H)J^J^_ 		] <<##$46KLDt*x/'9wiu<MMK!!##kk+*?*?*?*QR#
B7;;KH #E
I 1$Y!9):J:J9KLMM  :
	
 	1$I,,-BFF7B'++GR8Evr*I&9!uugQyk8TUN
 ((A-2F.J_J_JeJeJg2g#JJx xG &9  /1 !')24 	KKPQJJrNuugQyk8TUL
 &&!+0DH[H[HaHaHc0c#YZI !67 #)/	5)WU,2 Nqa
mv6M N N&)-&8#&!+w:ORS:S/Sio&deQz]AfIqQWyY]Z]_&N &  "Q&!4]GZfg
 jp&deQz]AfIqQWyY]Z]_&N &  >>*-!"J$~5%)!5F5'9+WYKW_*`a '"'&

 &00A5:NR\RcRcRiRiRk:k!

2"b(
 %~55#)L !:&| \ $%67 !&N A%#N&N/LNNgirs A% KKop6L 6L #"+"..,#6,!2 # +" 	 '((8&9EE&"-'(uu->'C" )+%"
( +<+@+@B+OF'(
(0MN
OOi  	]NNWY[\\	]| !O&&t"s7   B_" ``6`,`0`"	`+``c                 R   t        |       }g }|D ]  }|j                  dd      }|j                  d      s'|j                  d      }|j                  d      }|j	                  d      }|dk(  r`|d| }||d	z   d }	|j                  |j                  d
d      |||	d        d|iS )u  활성 worktree 목록 반환.

    task/<task_id>-<team_id> 패턴 브랜치를 가진 worktree만 포함.

    Args:
        project_path: git 저장소 루트 경로.

    Returns:
        {"worktrees": [{"path": str, "branch": str, "task_id": str, "team_id": str}]}
    r7   rK   zrefs/heads/task/zrefs/heads/rY   rZ   r6   Nr   rG   r  r7   rV   rW   rO   )rU   r   r   removeprefixrfindrL   )
r1   rawr/   r   
branch_refshort_branch	task_partdash_idxt_idtm_ids
             r   cmd_listr  ^  s     \
*C#%F 
VVHb)
$$%78!..}= !--g6	 ??3'r>(#(Q,.)z2.& 		
'
8   r+   c           	      <   t        |       }t        |       }	 t        ddd|dg|       }|j                         D ch c]#  }|j	                         s|j	                         % }}g }g }|d   D ]  }|d   }	|d   }
|d   }|d	   }|	|v rNt        dd
dd|
g|        t        ddd|	g| d       t        dddd|	g| d       |j                  |
|	||d       i|j                  |
|	||dd        ||dS c c}w # t
        j                  $ r t               }Y w xY w)uU  머지 완료된 워크트리를 자동 정리.

    main 브랜치에 이미 머지된 task/ 브랜치의 워크트리만 제거한다.
    머지되지 않은 워크트리는 건드리지 않는다 (안전장치).

    Args:
        project_path: git 저장소 루트 경로.

    Returns:
        {"cleaned": [...], "skipped": [...]}
    r3   r7   z--mergedr8   r4   rO   r  rV   rW   rG   r]  r^  r_  Fr-   r   r   r`  r  
not_merged)r  r7   rV   rW   r   )cleanedr   )
r  rE   r0   r?   r.   r'   r>   setr*   rL   )r1   listedr   
merged_rawrB   merged_branchesr  r   r   r7   r   rV   rW   s                r   cmd_cleanupr    sv    l#F$\2K Hj+7RS

 /9.C.C.ES1779SS %'G$&G[! 'HV*Y-Y-_$
HiA  $/ 
 *f= 
 NN#$&&	 NN#$&&*?'R 733a T((  % s(   $C; C6C6$C; 6C; ;DDc           	      6   t        |       }|d   D cg c]  }|d   |k(  s| }}g }t        |       }|D ]  }|d   }|d   }		 t        g d|      }
t        |
j	                         D cg c]  }|j                         s| c}      }	 t        dd	d
| d|	 g|      }|j                         rt        |      nd}|j                  ||	||d        d|iS c c}w c c}w # t        j                  $ r d}Y qw xY w# t        j                  $ r d}Y Xw xY w)u4  특정 task_id에 해당하는 모든 worktree 상태 반환.

    Args:
        project_path: git 저장소 루트 경로.
        task_id: 태스크 식별자 (team_id 무관하게 검색).

    Returns:
        {"worktrees": [{"path": str, "branch": str, "changed_files": int, "commits_ahead": int}]}
    rO   rV   r  r7   )r3   re   rI   r4   r   r3   zrev-listz--countz..)r  r7   r   commits_ahead)r  rE   r0   r   r?   r.   r'   r>   isdigitr   rL   )r1   rV   r  wmatchingr/   r   r   r   r7   
status_outrQ   r   	ahead_outr  s                  r   
cmd_statusr    sU    l#F!+.Ja!I,'2IJHJ#%F$\2K $
V*H	0J  *2G2G2I Z$TZZ\ Z[M
	"m2fX.	 I /8.?.?.AC	NqM 	 !.!.		
;$
L   W K ![,, 	M	 ,, 	M	sF   CC&C%*C 
 C 
C%3D  C%%C=<C= DDr   r   c                   |}|rt        |      nt        |       dz  dz  }|| dz  }d}d}	|j                         sddddddS 	 t        j                  |j	                  d	            }
|
j                  d
      }t        g d| d       	 t        ddd|g|       }	|sdd||	ddS ||	k(  rdd||	ddS dd|dd  d|	dd  ||	ddS # t        $ r}dd| ddddcY d}~S d}~ww xY w# t        j                  $ r dd|dddcY S w xY w)u  worktree base SHA가 현재 origin/main SHA와 일치하는지 검증 (stale base PR 방지).

    task-2700 Phase 2: spawn 시 base SHA 검증으로 PR #158류 stale base 방지.

    Args:
        project_path: git 저장소 루트 경로.
        task_id: 태스크 식별자.
        team_id: 팀 식별자 (마커 경로 탐색에는 미사용, 인터페이스 일관성용).
        base_ref: 비교 대상 origin 참조 (기본 "origin/main").
        events_dir: base 마커가 저장된 디렉터리. None이면 <project_path>/memory/events.

    Returns:
        {"ok": bool, "reason": str, "marker_base_sha": str|None,
         "current_origin_sha": str|None, "stale": bool}
    r   r   r   NFzbase marker missing)okr   marker_base_shacurrent_origin_shastaler   r   r   zbase marker read error: r   r-   r3   r;   r   r4   zcannot resolve origin/mainz>base marker has no base_sha (likely base_fallback at creation)Tz$base SHA matches current origin/mainzstale base: marker=   u    ≠ origin/main=)r   r   r   r   r   r   r   r*   r0   r'   r>   )r1   rV   rW   r   r   rS   r   r   r  r  r   r   s               r   verify_spawn_baser    s   . 	A&0$z"d<6H86SV^6^KG9,?!@@L"&O%)  +#"&
 	


zz,"8"8'"8"JK&**:6 		#UC
!KX6
 V."4
 	
 ,,<."4
 	
 %obq&9%: ;##5bq#9":<  /"4	
 		
_  
05#"&
 	

$ (( 
2."&
 	

s0   6C	 C* 		C'
C"C'"C'*DDc                     t        j                  dd      } | j                  dd      }|j                  dd	      }|j	                  d
d	       |j	                  dd	       |j	                  dd	       |j	                  ddd       |j                  dd	      }|j	                  d
d	       |j	                  dd	       |j	                  dd	       |j	                  ddg dd       |j	                  ddd       |j	                  ddd       |j	                  d d!d"       |j	                  d#d!d$       |j	                  d%t
        d&d'(       |j                  d)d*	      }|j	                  d
d	       |j                  d+d,	      }|j	                  d
d	       |j                  d-d.	      }|j	                  d
d	       |j	                  dd	       |j                  d/d0	      }|j	                  d
d	       |j	                  dd	       |j	                  dd	       |j	                  d1d2d3       |j	                  d4dd5       | S )6u   argparse 파서 구성.zworktree_manager.pyu6   Git Worktree 관리 스크립트 (멀티봇 환경용))progdescriptioncommandT)destrequiredcreateu   새 worktree 생성)helpr1   u   git 저장소 루트 경로rV   u   태스크 ID (예: 329.1)rW   u   팀 ID (예: dev1)z--no-copy-env
store_trueuO   워크트리 생성 시 .env 파일 자동 복사 건너뛰기 (기본: 복사))rL  r  finishu   worktree 작업 완료u   태스크 IDu   팀 IDz--action)rW  rb  rS  rR  rQ  u)   완료 액션: merge|discard|keep|pr|auto)r  choicesr  z
--worktreeNuA   worktree 경로 정식 지정 (TASKCTL_CWD env deprecated 대체))defaultr  z
--pr-titlerK   u	   PR 제목z	--pr-bodyu	   PR 본문z--gemini-timeout,  u    Gemini 리뷰 대기 시간(초))typer  r  cleanupu'   머지 완료된 worktree 자동 정리rH   u   활성 worktree 목록re   u   worktree 상태 확인verify-baseuU   worktree base SHA가 현재 origin/main과 일치하는지 검증 (stale base 방지)z
--base-refr   u1   비교 기준 origin 참조 (기본: origin/main)z--events-diru?   base 마커 디렉터리 (기본: <project_path>/memory/events))argparseArgumentParseradd_subparsers
add_parseradd_argumentr   )parsersubp_createp_finish	p_cleanupp_listp_statusp_vbases           r   build_parserr  w  s   $$"LF 

Y

>C ~~h-B~CH./LM)*EF)*>?^   ~~h-E~FH./LM).9)(3:8	   P  
 P  
 ,E+rD,3Jlm y/XYI>0MN ^^F)A^BF
-JK ~~h-E~FH./LM).9 nnd  G .KL82@  
 N   Mr+   c                     t               } | j                         }	 |j                  dk(  r;t        |j                  |j
                  |j                  t        |dd             }nK|j                  dk(  r\t        |j                  |j
                  |j                  |j                  t        |dd      t        |dd      t        |d	d
            }n|j                  dk(  rt        |j                        }n|j                  dk(  rt        |j                        }n|j                  dk(  r!t        |j                  |j
                        }nf|j                  dk(  rEt        |j                  |j
                  |j                  t        |dd      t        |dd            }ndd|j                   d}t)        t+        j,                  |dd             |j                  dk(  r(t/        j0                  |j3                  d      rdnd       |j3                  d      dk(  rt/        j0                  d       yy# t        j                  t         t"        t$        f$ r}dt'        |      d}Y d}~d}~ww xY w)u   CLI 메인 진입점.r  no_copy_envF)r   r  rM  rK   rN  rO  r  )rM  rN  rO  r  rH   re   r  r   r   r   Nr  rc   zUnknown command: rd   r   r   r  r      r   )r  
parse_argsr  r   r1   rV   rW   getattrr  rL  r  r  r  r  r'   r>   r~  r   r   r^   printr   r   sysexitr   )r  r   r/   r   s       r   r9   r9     s   ^FD&: <<8#!!$T=%@@	F \\X%!! z26i4&t-=sCF \\Y& !2!23F\\V#d//0F\\X% 1 14<<@F\\]*&!! z=A"4t<F !(6G~4VWF
 
$**V%
:; ||}$fjj&A.zz(w& ' ))<WM :#C9:s   FH4 4#I/I**I/__main__)NTT)N)FTr   TN)F)rK   rK   r  F)r   N):__doc__r  rx   r   r   r   r'   r  r   pathlibr   typingr   r^   r   rI  r   _WS_ROOTr  insertutils.blast_radius_parserr   r   r   r   rp   r   rH   r   CompletedProcessr*   r0   rE   rU   r\   r_   rz   r   r   r   r   r   r   r  r+  r6  __annotations__r:  rD  rK  r  r  r  r  r  r  r  r9   r[   r+   r   <module>r     s       	  
    tH~%%'//23388HHOOAx  8			8	$4S> 4 	
s)	t  	
   %6!tCy !sTz !S !'S 'S 'T"S "T$sCx.-A "T' 's 's '
K Kc KC KC K"V Z ## ## #$s) #L 
 ! $!]]] ] 	]
 ] ] ] d
] 
#s(^] ]@' ' 'TKC K K3 KSV K\:c :# :# :C :TXY]^acf^fYgTh :| NS1J4S>"1J361JFJ1J	#s(^1Jh" T#Y .
# d3i 
.T#s(^(< .D .]abfgjlogobp]q .bLT$sCx.%9 Ld L2  KPKPKP KP 	KP
 KP KP KP KP 
#s(^KP KP\
*!3 *!4S> *!ZE4c E4d38n E4P6!S 6!3 6!4S> 6!| "!c
c
c
 c

 c
 d
c
 
c
VMh-- M`4n zF r+   