
    ivI              
          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	m
Z
mZ ddlmZ ddlZ ee      j!                         j"                  Z ee      ej(                  vr"ej(                  j+                  d ee             ddlmZ ej2                  j5                  dd      Z	 ej(                  j+                  de       ddlmZ  e e
d	
            Z e ee      dz  dz        Z  ejB                  d      Z" ejB                  d      Z# ejB                  d      Z$ ejB                  d      Z%d(dedz  de&eef   fdZ'dededededz  ddf
dZ(dededdfdZ)dededdfdZ*defdZ+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dfd"Z0dededz  fd#Z1d$ede-fd%Z2d)d&Z3e4d'k(  r e3        yy# e$ r dZY w xY w)*uC   팀장 → 아누 완료 통보 스크립트 (체인 인식 버전)    N)datetime	timedeltatimezone)Path)format_notification_messageWORKSPACE_ROOTz/home/jay/workspace)sync_task_completion	   )hourslogszdone-protocol.logz^[a-z0-9\-]+$z^[a-zA-Z0-9/_\-\.]+$z^(normal|critical|security)$z^task-\d+\.\d+$env_filereturnc                 8   | 	t          d} i }t        |       }|j                         s|S 	 |j                  d      j	                         D ]  }|j                         }|r|j                  d      r'|j                  d      r|t        d      d j                         }d|vrY|j                  d      \  }}}|j                         }|j                         }t        |      dk\  r|d	   |d
   k(  r|d	   dv r|dd
 }|s|||<    	 |S # t        $ r Y |S w xY w)uV  `.env.keys` 파일을 파싱하여 환경변수 딕셔너리를 반환한다.

    지원 형식:
        export KEY=VALUE
        KEY=VALUE
        export KEY="VALUE"   (따옴표 제거)
        export KEY='VALUE'   (따옴표 제거)

    주석(#)과 빈 줄은 무시한다.
    파일이 없으면 빈 딕셔너리를 반환한다.
    Nz
/.env.keysutf-8encoding#zexport =   r   )"'   )
r   r   exists	read_text
splitlinesstrip
startswithlen	partitionOSError)r   resultpathraw_linelinekey_values           0/home/jay/workspace/scripts/notify-completion.pyload_env_keysr*   )   s.    $%Z0F>D;;=8CCE 	$H>>#D4??3/y)C	N,-335$ NN3/MCE))+CKKME5zQ58uRy#8U1X=Sa#s!	$( M  Ms   CD D 	DDteam	task_filelevelnext_task_idc                 "   t         j                  |       s.d| }t        d| t        j                         t        |      t        j                  |      s.d|}t        d| t        j                         t        |      t        j                  |      s.d|}t        d| t        j                         t        |      |Dt        j                  |      s.d|}t        d| t        j                         t        |      yy)uF  dispatch 인자를 검증한다. 패턴 불일치 시 ValueError를 발생시킨다.

    Args:
        team: 팀 이름 (예: "dev1-team")
        task_file: 태스크 파일 경로
        level: 우선순위 ("normal" | "critical" | "security")
        next_task_id: 다음 태스크 ID (예: "task-566.2") 또는 None
    u7   dispatch 인자 검증 실패 - team 패턴 불일치: zERROR: fileu<   dispatch 인자 검증 실패 - task_file 패턴 불일치: u8   dispatch 인자 검증 실패 - level 허용값 초과: Nu?   dispatch 인자 검증 실패 - next_task_id 패턴 불일치: )	_RE_TEAMmatchprintsysstderr
ValueError_RE_TASK_FILE	_RE_LEVEL_RE_TASK_ID)r+   r,   r-   r.   msgs        r)   _validate_dispatch_argsr<   U   s     >>$GxPuoCJJ/oy)LYMZuoCJJ/o??5!H	RuoCJJ/o(9(9,(GOP\O_`uoCJJ/o )H    task_idmessagec                 \   t        j                  t              j                         }d| d|  d| d}	 t	        t
              }|j                  j                  dd       t        t        |      dd	      5 }|j                  |       d
d
d
       y
# 1 sw Y   y
xY w# t        $ r Y y
w xY w)u3   done-protocol.log에 상태 전이를 기록한다.[z] [notify-completion] z: 
Tparentsexist_okar   r   N)r   nowKST	isoformatr   DONE_PROTOCOL_LOGparentmkdiropenstrwriter!   )r>   r?   tsr%   log_pathfs         r)   log_protocolrS   x   s    	c		$	$	&Brd(	G9B?D)*dT:#h-w7 	1GGDM	 	 	 s0   AB 8B
B BB B 	B+*B+c                    t        t         d|  d      }	 |j                  j                  dd       |j	                  |d       t        | dt        |       d       y
# t        $ r}t        | d	|        Y d
}~y
d
}~ww xY w)us   완료 메시지를 파일로 저장한다. 아누가 .done 감지 시 이 파일을 읽어 그대로 전달한다./memory/events/z.completion.txtTrC   r   r   u   .completion.txt 저장 완료 (z chars)u   .completion.txt 저장 실패: N)r   r   rK   rL   
write_textrS   r   r!   )r>   r?   completion_filees       r)   _save_completion_messagerY      s    n-_WI_UVOE$$TD$A""7W"=W ?G~WUV EW ?sCDDEs   A	A! !	B*A>>Bc                      t         j                  j                  d      } | s0t        dt        j
                         t	        j                  d       | S )u=   ANU_KEY를 환경변수에서 가져온다. 없으면 종료.COKACDIR_KEY_ANUu   [FATAL] COKACDIR_KEY_ANU 환경변수가 설정되지 않았습니다. 아누 세션 인증키가 필요합니다. 해결: 'source /home/jay/workspace/.env.keys'를 실행하거나 '--anu-key <key>' 옵션으로 직접 전달하세요.r0   r   )osenvirongetr4   r5   r6   exit)r&   s    r)   get_anu_keyr`      sA    
**..+
,CE 	
 	Jr=   c                    dt          ddd| g}	 t        j                  |ddd      }|j                  dk(  rt	        j
                  |j                        S 	 d	d	d
d
dS # t        j                  t        j                  f$ r Y ,w xY w)u?   chain_manager.py check 호출하여 체인 소속 여부 확인python3/chain_manager.pycheck	--task-idT   capture_outputtexttimeoutr   FN)in_chainis_lastchain_idr.   	r   
subprocessrun
returncodejsonloadsstdoutTimeoutExpiredJSONDecodeErrorr>   cmdr"   s      r)   check_chain_statusry      s     	
+,CDtRP!::fmm,, "
 %TSWXX %%t';';< s   AA #BBchat_idc           	         t         j                  j                  d      }|st        dd       yd| d}| |dd}t	        d	      D ]  }	 t        j                  ||d
      }|j                  dk(  r y|j                  dk(  rI| |d}t        j                  ||d
      }|j                  dk(  r yt        dd|j                           y|j                  dk(  r;t        |j                  j                  dd            }	t        j                  |	       t        dd|j                           y# t
        j                  $ r8}
t        dd|dz    d|
        t        j                  d|z         Y d}
~
1d}
~
ww xY w)uB   직접 Telegram Bot API 호출 (토큰 0, Claude 세션 미생성)ANU_BOT_TOKEN u,   WARN: ANU_BOT_TOKEN 미설정, 알림 스킵Fzhttps://api.telegram.org/botz/sendMessageMarkdown)rz   ri   
parse_mode   
   )rr   rj      Ti  )rz   ri   u"   WARN: Telegram fallback도 실패 i  zRetry-After   zWARN: Telegram API u&   WARN: Telegram 전송 실패 (attempt r   ): r   N)r\   r]   r^   rS   rangerequestspoststatus_codeintheaderstimesleepRequestException)rz   r?   	bot_tokenurlpayloadattemptrespfallbackfallback_respretry_afterrX   s              r)   send_telegram_notificationr      sr   

/IRGH(<
@C!7*MG8 #	#==7B?D3&3&'.@ (c" M ,,3R#EmF_F_E`!ab3&!$,,"2"2=!"DE

;'243C3C2DEF##, 	 (( 	#EgPQk]RUVWUXYZJJq'z""	#s1   
'D13;D10D1A	D1D11E<-E77E<c                    dt          ddd| g}	 t        j                  |ddd      }|j                  dk(  rt	        j
                  |j                        S 	 d	d
iS # t        j                  t        j                  f$ r Y d	d
iS w xY w)u?   chain_manager.py next 직접 호출하여 다음 Phase dispatchrb   rc   nextre   T<   rg   r   actionerrorrn   rw   s      r)   dispatch_next_phaser      s     	
+,CDtRP!::fmm,, " g %%t';';< gs   AA #BBc           	          dt          dd| g}	 t        j                  |ddd       y# t        j                  $ r+ t	        d|  dt          d	|  t
        j                  
       Y yw xY w)u   task-timer end 호출rb   z/memory/task-timer.pyendTrf   rg   uW   WARNING: task-timer end 호출이 30초 초과로 타임아웃되었습니다 (task_id=u)   ). 수동으로 실행하세요: python3 z/memory/task-timer.py end r0   N)r   ro   rp   ru   r4   r5   r6   )r>   rx   s     r)   end_task_timerr      s     	
/0	C
s4dBG$$ 
efmen o55C4DD^_f^gi	

s   ) ;A'&A'c                    	 t        t         d      }|j                         rXt        j                  |j                  d            }|j                  di       j                  | i       }|j                  d      S 	 y# t        $ r Y yw xY w)u0   task-timers.json에서 task_id로 team_id 조회z/memory/task-timers.jsonr   r   tasksteam_idN)r   r   r   rr   rs   r   r^   	Exception)r>   
timer_filedata	task_datas       r)   get_team_id_from_taskr      s    ^,,DEF
::j22G2DED"-11'2>I==++    s   A9A> >	B
	B
r   c                 J   	 ddl m}  || d       y# t        $ r Y n6t        $ r+}t	        d|  d| t
        j                         Y d}~y	d}~ww xY w	 t        t         d
      }|j                         sy	t        |dd      5 }t        j                  |      }ddd       n# 1 sw Y   nxY w| j                  di       v rt        j                  t         j"                        j%                  d      }d|d   |    d<   ||d   |    d<   |j'                  d      }t        |dd      5 }t        j(                  ||dd	       ddd       n# 1 sw Y   nxY w|j+                  |       y	 y	# t        $ r+}t	        d|  d| t
        j                         Y d}~y	d}~ww xY w)uZ   bot-activity.json의 봇 상태를 idle로 설정

    Returns:
        성공 여부
    r   )set_bot_statusidleTu#   WARNING: set_bot_idle 실패 (team=r   r0   NFz /memory/events/bot-activity.jsonrr   r   botsz%Y-%m-%dT%H:%M:%SZstatussincez.tmpwr   )indentensure_asciiu1   WARNING: set_bot_idle 직접 구현 실패 (team=)utils.bot_activityr   ImportErrorr   r4   r5   r6   r   r   r   rM   rr   loadr^   r   rG   r   utcstrftimewith_suffixdumpreplace)r   r   rX   bot_activity_filerR   r   utc_now	temp_files           r)   set_bot_idler     s   
5w'  3G9CsC#**U
 N#33S!TU '')#S7; 	 q99Q<D	  	  	  dhhvr**ll8<<099:NOG.4DL!(+-4DL!'* *55f=Iiw7 A1		$!%@A A A/0 + 	  A'#aSQX[XbXbcsh    	AA!AA"E. 7E. B$	E. $B-)BE. +E	E. EE. .	F"7!FF"c            	      N   t        j                  d      } | j                  dd       | j                  dt        j                  j                  dd      	       | j                  d
d 	       | j                         }|j                  xs  t        j                  j                  dd      }t        rK	 t        |j                        }|j                  d      r$t        d|d    d|j                  dd              t        |j                        }|j                  d      r|j                  d      smt        d|j                   d|j                  d       d       t        |j                  d|j                  d       d       t!        |j                        }t        dt#        j$                  |d              |j                  d      dk(  r|j                  d      }|j                  d       }|j                  d!d"      }	|j                  d      }
|r|r	 t'        |||	|
       t+               }i t        j                  |}d$t,         d%d&|d'|d(|	g}|
r|j/                  d)|
g       	 t1        j2                  |d*d*d+|,      }|j4                  d-k7  r?t        |j                  d.       t        d/|j                   t        j                         nt        d0|        n	 |j                  dd3      }t9        t,         d4|j                   d5      }t9        t,         d6|j                   d7      }d }|j;                         r&	 t#        j<                  |j?                  d89            }tE        |j                  ||      }|jG                  d:|j                   d;d:|j                   d<| d=d>      }tI        |j                  |       tK        |jL                  |      rt9        t,         d6|j                   d?      }|jN                  jQ                  d*d*@       	 t        jR                  tU        |      t        jV                  t        jX                  z  t        jZ                  z        }t        j\                  |       t        |j                  dA       n|j                  d      rCt        d|j                   dC       t        |j                  dD|j                  d       d       n/t        dE|j                   dF       t        |j                  dG       t9        t,         d4|j                   d5      }t9        t,         d6|j                   d7      }d }|j;                         r&	 t#        j<                  |j?                  d89            }tE        |j                  ||      }tI        |j                  |       tK        |jL                  |      rt9        t,         d6|j                   d?      }|jN                  jQ                  d*d*@       	 t        jR                  tU        |      t        jV                  t        jX                  z  t        jZ                  z        }t        j\                  |       t        |j                  dA       ta        |j                        }|rb	 tc        |      rt        |j                  dH| d       n:t        |j                  dI| d       t        dJ| dt        j                         y y t        |j                  dN       y # t        $ r)}t        d| t        j                         Y d }~Vd }~ww xY w# t(        $ r t        |j                  d#       Y y w xY w# t0        j6                  $ r5 t        |j                  d1       t        d2t        j                         Y w xY w# t"        j@                  tB        f$ r Y sw xY w# t^        $ r t        |j                  dB       Y ww xY w# t"        j@                  tB        f$ r Y w xY w# t^        $ r t        |j                  dB       Y w xY w# t        $ rD}t        |j                  dK|        t        dL| dM| t        j                         Y d }~y d }~ww xY w)ONu/   팀장 → 아누 완료 통보 (체인 인식))descriptionr>   u   작업 ID (예: task-381.1))helpz	--chat-idCOKACDIR_CHAT_ID
6937032012)defaultz	--anu-keyr[   r}   updatedz[TODO] u   건 업데이트: detailsu   WARNING: todo sync 실패: r0   rk   rl   z[CHAIN] u   : 체인 중간 Phase (chain=rm   )zchain middle dispatch (chain=u   [CHAIN] dispatch 결과: F)r   r   dispatchr,   r+   r-   normalu4   dispatch 인자 검증 실패 (셸 인젝션 방지)rb   z/dispatch.pyz--teamz--task-filez--levelre   Tr   )rh   ri   rj   envr   u   dispatch 실패u   ERROR: dispatch 실패: u(   [CHAIN] dispatch.py 호출 성공: team=u   dispatch 타임아웃u   ERROR: dispatch.py 타임아웃unknownz/memory/reports/z.mdrU   z.doner   r   z**u    완료 보고**u    완료 보고** (chain: u!   ). 다음 Phase 자동 위임됨.r   z.done.notifiedrC   u   .done.notified 마커 생성u#   .done.notified 마커 이미 존재u0   : 체인 마지막 Phase → 텔레그램 알림z"chain last telegram notify (chain=z	[NOTIFY] u'   : 일반 작업 → 텔레그램 알림z independent task telegram notifyu   bot idle 전환 성공 (team=u   bot idle 전환 실패 (team=u&   WARNING: bot idle 전환 실패 (team=u   bot idle 전환 예외: u&   WARNING: bot idle 전환 예외 (team=r   u&   team_id 없음, bot idle 전환 스킵)2argparseArgumentParseradd_argumentr\   r]   r^   
parse_argsanu_keyr	   r>   r4   r   r5   r6   ry   rS   r   rr   dumpsr<   r7   r*   r   extendro   rp   rq   ru   r   r   rs   r   rv   r!   _format_notification_messager   rY   r   rz   rK   rL   rM   rN   O_CREATO_EXCLO_WRONLYcloseFileExistsErrorr   r   )parserargsr   sync_resultrX   
chain_infodispatch_resultr,   r+   r-   r.   env_keys
merged_envdispatch_argvr"   rm   _report_path
_done_path
_done_datar?   notified_pathfdr   s                          r)   mainr   3  s   $$1bcF
	(EF


1<@      D llDbjjnn-?DG 	F.t||<Ky)I 677I+//ZcegJhIijk
 $DLL1J ~~j!*..*C&CJNNS]D^C__`abLL+JNN:,F+GqI	
 .dll;)$**_SX*Y)Z[\ x(J6'++K8I"&&v.D#'':E*..y9LT+D)ULQ )?7

7h7
 %&l3!	!  !((+|)DE'^^%'+! "&F ((A-$ LL- 6v}}oF!$
  HOP  >>*i8~..>t||nCPQ^,ODLL>OP

!ZZ
(<(<g(<(NO
 /t||\:V//./7zAbc

 	!w7%dllG< N#3?4<<.P^!_`M  &&td&CRWWS/bii1G"++1UVT\\+IJ >>*%HT\\N*Z[\4Z^^J5O4PPQR
 Idll^+RST'IJ~..>t||nCPQ^,ODLL>OP

!ZZ
(<(<g(<(NO
 /t||\:V w7%dllG< N#3?4<<.P^!_`M  &&td&CRWWS/bii1G"++1UVT\\+IJ
 $DLL1G	]G$T\\-J7)ST+UVT\\-J7)ST+UV>wiqIPSPZPZ[ W 	T\\#KLy  	F/s3#**EE	F: "  /efR "00  / 9 ZZ ( (('2 " # RT\\+PQR, (('2  # RT\\+PQR  	])A!'EF:7)3qcJQTQ[Q[\\	]s   3A
\& !] 4A6^  %_ 'A:_+ 7%` A:`1 .Aa &	]/]]]=<]= A___('_(+```.-`.1aa	b$ :bb$__main__)N)r   N)5__doc__r   rr   r\   reshlexro   r5   r   r   r   r   pathlibr   r   __file__resolverK   _SCRIPTS_DIRrN   r#   insertreport_utilsr   r   r]   r^   r   utils.todo_syncr	   r   rH   rJ   compiler2   r8   r9   r:   dictr*   r<   rS   rY   r`   ry   boolr   r   r   r   r   r   __name__ r=   r)   <module>r      se   I   	 	   
  2 2   H~%%'..|CHH$HHOOAs<() T 02GH HHOOA~&4 yq!"^,v58KKL  2::&'

23BJJ67	bjj+,)C$J )$sCx. )X 
     *	 
 
 F
# 
 
 
Ec EC ED ES Y Y Y&   c  d  F  $
C 
D 
$
3 
3: 
*# *$ *ZrMj zF W    s   0"G   G
	G
