
    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mZ ddlm	Z	 ddl
mZmZmZ 	 ddlmZ  ee      Z ej0                  d      Zh dZh d	Zd
edefdZdedefdZddddddZ G d d      Z ddZ!edk(  r e!        yy# e$ rE ej"                  j%                  d e e	e      j*                  j*                               ddlmZ Y w xY w)u  
작업 시간 추적 시스템

Usage:
    python3 memory/task-timer.py start <task_id> [team_id] [description]
    python3 memory/task-timer.py end <task_id>
    python3 memory/task-timer.py status <task_id>
    python3 memory/task-timer.py list [status]
    python3 memory/task-timer.py progress [--project <project_id>]
    python3 memory/task-timer.py log <message>
    python3 memory/task-timer.py cleanup [--running-hours 2] [--reserved-minutes 30] [--dry-run]
    python3 memory/task-timer.py cross-start <agent_name> --task <task_id> [--desc <description>] [--team <team_id>]
    python3 memory/task-timer.py cross-end <agent_name>
    Ndatetime)Path)AnyDictOptional)
get_loggerz/^task-\d+(\.\d+)?(_\d+\.\d+)?(_[a-z])?(\+\d+)?$>   	dev1-team	dev2-team	dev3-team	dev4-team	dev5-team	dev6-team	dev7-team	dev8-team
anu-direct design	composite	marketing
consulting
publishing>   lokimaatjanusvenustask_idreturnc                 >    t        t        j                  |             S )uB   task_id 형식 검증: 포맷 v2 지원 (task-N[.N][_P.P][_x][+R]))boolTASK_ID_PATTERNmatch)r   s    B/home/jay/workspace/.worktrees/task-2116-dev1/memory/task-timer.pyvalidate_task_idr$   9   s    %%g.//    team_idc                     | t         v S )u8   team_id 형식 검증: 허용 목록에 있는지 확인)ALLOWED_TEAM_IDS)r&   s    r#   validate_team_idr)   >   s    &&&r%   u   의사결정u   시스템 변경u   아키텍처 논의u   위임 기록)notedecisionsystemarchdispatchc                      e Zd ZdZd3dee   ddfdZdefdZd4dZ		 d5ded	ed
edededefdZ
dedee   fdZd6dededefdZdedee   fdZd3dee   defdZd3de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dededededdf
dZdedefdZd3d ee   defd!Zd"eddfd#Zd"ed$eddfd%Zdefd&Zd'eddfd(Zd7d)eded
ed*edef
d+Zd)edefd,Zd8d-ed.ede fd/Z!d9d0ed1edefd2Z"y):	TaskTimeru   작업 시간 추적 관리자Nworkspace_pathr   c                 J   | t         j                  j                  dd      }t        |      | _        | j                  dz  dz  | _        | j                  dz  dz  | _        | j                  dz  dz  | _        | j                  dz  dz  | _        | j                         | _
        y )NWORKSPACE_ROOTz/home/jay/workspacememoryztask-timers.jsondailyzpipeline-status.jsonzcross-functional-status.json)osenvirongetr   r1   
timer_filedaily_log_dirpipeline_status_filecross_functional_file_load_timerstimers)selfr1   s     r#   __init__zTaskTimer.__init__P   s    !ZZ^^,<>STN">2--8;MM!008;gE$($7$7($BE[$[!%)%8%88%CFd%d"'')r%   c                 <   | j                   j                         r	 | j                   j                  dz  }|j                  j                  dd       t	        |d      5 }t        j                  |t
        j                         	 t	        | j                   dd      5 }t        j                  |      cddd       t        j                  |t
        j                         cddd       S di iS # 1 sw Y   nxY w	 t        j                  |t
        j                         n)# t        j                  |t
        j                         w xY w	 ddd       n# 1 sw Y   nxY wyy# t        $ r3}t        j                  d	| j                    d
|        di icY d}~S d}~ww xY w)u   타이머 데이터 로드.task-timers.lockTparentsexist_okwrutf-8encodingNu    타이머 파일 로드 실패:  - tasks)r9   existsparentmkdiropenfcntlflockLOCK_SHjsonloadLOCK_UN	Exceptionloggererror)r?   	lock_pathlock_fdfes        r#   r=   zTaskTimer._load_timersZ   sP   ??!!#% OO225HH	  &&td&C)S) <WKK7<!$//3I 0Q#'99Q<0 0 GU]];< < R= 0 0 0 GU]];GU]];;< < < <  %??PPSTUSVWX}$%sr   AE %EDC,1	D:$E	E ,C5	1D9%E&EE	E EE 	F((FFFc                    ddl }	 | j                  j                  j                  dd       | j                  j                  dz  }t	        |d      5 }t        j                  |t
        j                         	 |j                  d| j                  j                  ddd	
      5 }t        j                  | j                  |dd       |j                          t        j                  |j                                ddd       t        j                   j"                  t%        | j                               t        j                  |t
        j&                         	 ddd       y# 1 sw Y   jxY w# t        j                  |t
        j&                         w xY w# 1 sw Y   yxY w# t(        $ r/}t*        j-                  d| j                   d|        Y d}~yd}~ww xY w)u-   타이머 데이터 저장 (원자적 쓰기)r   NTrC   rB   rF   F.tmprH   dirdeletesuffixrJ      ensure_asciiindentu    타이머 파일 저장 실패: rK   )tempfiler9   rN   rO   rP   rQ   rR   LOCK_EXNamedTemporaryFilerT   dumpr>   flushr6   fsyncfilenoreplacenamestrrV   rW   rX   rY   )r?   	_tempfilerZ   r[   tmpr]   s         r#   _save_timerszTaskTimer._save_timersm   sk   $	UOO""(((E..1DDIi% 8GU]]38"55 OO22$%!( 6  	/ 		$++sqQ		.	/ JJsxxT__)=>KK78 8	/ 	/ KK78 8   	ULL;DOO;LCPQsSTT	Usg   AF# %F8*E."AE"9;E.4$FF# "E+	'E..&FFF F#  F# #	G,%GGr   r&   description
project_id
work_levelc           
      J   t        |      s"t        j                  d| d       dd| ddS |rGt        |      s<t        j                  d| dd	j	                  t
        d
hz
         d       dd| ddS | j                  d   j                  |      }|r6|j                  d      dk(  r"t        j                  d| d       d||d   dS |r5|j                  d      dk(  r!t        j                  d|        dd| ddS |r5|j                  d      dk(  r!t        j                  d|        dd| ddS t        j                         }||||||j                         dddd	| j                  d   |<   t        j                  d| d | d       | j                          | j                  d!| j                  d   |          |s|rF|rd"|j                          d#nd
}d$|j                  d%       d#| | d&| d'}	| j!                  |	       d(||j                         dS ))u   작업 시작u   잘못된 task_id 형식: z% (expected: task-N[.N][_P.P][_x][+R])rY   Invalid task_id format: '%'. Expected: task-N[.N][_P.P][_x][+R]statusreasonu   잘못된 team_id:  (allowed: , r   )zInvalid team_id: 'za'. Allowed: dev1-team~dev8-team, marketing, consulting, design, publishing, composite, anu-directrL   r|   runningu   이중 등록 시도 거부: u   는 이미 running 상태already_running
start_time)r|   r   r   	completedu+   완료된 task 덮어쓰기 시도 거부: z	task_id 'z%' is already completed. Use a new ID.staleu$   stale task 재시작 시도 거부: z"' is in stale state. Use a new ID.N)	r   r&   ru   rv   rw   r   end_timeduration_secondsr|   u   태스크 시작: z (team=start[] - [%H:%M:%S: z
 - startedstarted)r$   rX   warningr)   joinr(   r>   r8   r   now	isoformatinfort   _update_pipeline_statusupperstrftime_append_to_daily_log)
r?   r   r&   ru   rv   rw   existingr   	level_tag	log_entrys
             r#   
start_taskzTaskTimer.start_task   ss   
  (NN7y@efg!5gY>cd 
 +G4NN0	TYYO_cebfOfEgDhhijk!.wi  8Y  Z  ;;w'++G4X.);NN:7)C\]^/GS[\hSijj X.+=NNH	RS%9WIEj1kll X.'9NNA'KL%9WIEg1hii\\^
 &$$$..0 $
)
GW% 	(		CD$$Wdkk'.B7.KL k6@!J,,./r2bIj11*=>bG9TVWbVccmnI%%i0#zG[G[G]^^r%   c                     |dz   } j                   d   j                         D cg c],  \  }}|j                  |      r|j                  d      dk(  r|. }}}|syt	        |      dk(  r|d   S t        | fd	      S c c}}w )
u  task_id prefix로 running 상태인 항목을 찾아 실제 task_id 반환.

        - 정확 매칭이 존재하면 None 반환 (호출자가 직접 처리)
        - task_id + "." 로 시작하는 running 항목 검색
        - running 항목이 1개면 해당 task_id 반환
        - running 항목이 여러 개면 start_time이 가장 최신인 것 반환
        - 없으면 None 반환
        .rL   r|   r   N   r   c                 H    j                   d   |    j                  dd      S )NrL   r   r   )r>   r8   )tidr?   s    r#   <lambda>z/TaskTimer._fuzzy_find_running.<locals>.<lambda>   s"    DKK0599,K r%   key)r>   items
startswithr8   lenmax)r?   r   prefixr   data
candidatess   `     r#   _fuzzy_find_runningzTaskTimer._fuzzy_find_running   s     3 "[[1779
T~~f%$((8*<	*I 

 

 z?aa= K
 	

s   1B	qc_resultc           	      H   || j                   d   vr6| j                  |      }|!t        j                  d|        dd| ddS |}| j                   d   |   }|j	                  d      d	k(  rRt        j                  d
|        d	||d   |j	                  d      |j	                  d      |j	                  d      ddS t        j                         }t        j                  |d         }||z
  j                         }|j                         |d<   ||d<   | j                  |      |d<   d	|d<   |r|nd|d<   t        j                  d| d|d    d       | j                          | j                  d|       | j                  |j	                  dd             | j                  |j	                  dd             | j!                  ||||       |d   s|d   r8d|j#                  d       d|d    d|d    d|d    d	}| j%                  |       d	||d   |d   ||d   dS ) ut  작업 종료 (멱등성 보장: 이미 completed면 조용히 기존 결과 반환)

        정확 매칭이 없으면 task_id + "." prefix로 running 항목을 fuzzy match하여 종료.

        Args:
            task_id: 종료할 작업 ID
            qc_result: QC 결과 ("PASS", "FAIL", "WARN" 또는 빈 문자열). 빈 문자열이면 None으로 저장.
        rL   Nu    태스크를 찾을 수 없음: rY   zTask 'z' not foundr{   r|   r   u%   태스크 이미 완료됨 (멱등): r   r   r   duration_humanT)r|   r   r   r   r   r   already_completedr   u   태스크 완료:  (r   endrv   r   r&   ru   r   r   r   r   z - completed ()r|   r   r   r   r   r   )r>   r   rX   rY   r8   r   r   r   fromisoformattotal_secondsr   _format_durationrt   r   _update_project_progress_update_bot_activity_write_event_filer   r   )	r?   r   r   matched	task_datar   r   durationr   s	            r#   end_taskzTaskTimer.end_task   s    $++g....w7G?yIJ")vgYk5RSSGKK(1	 =="k1KK?yIJ%"'5%MM*5$-MM2D$E"+--0@"A%)  <<> ++Il,CD
 z)88: !) 2 2 4	*(0	$%&*&;&;H&E	"#)	(.7T	+(	I>N4O3PPQRS$$UI6%%immL"&EF 	!!)--	2">? 	w	8XF Y9]#;h//
;<By?S>TTVW`anWoVpp~  @I  JZ  @[  \  \]  ^I%%i0 "#L1!*- ('(89
 	
r%   c                    || j                   d   v r| j                   d   |   S |dz   }| j                   d   j                         D cg c]  \  }}|j                  |      s||f }}}|sy|D cg c]  \  }}|j                  d      dk(  s||f  }}}|rt	        |d       d   S t	        |d	       d   S c c}}w c c}}w )
u   작업 상태 조회.

        정확 매칭이 없으면 task_id + "." prefix로 fuzzy match:
        - running 상태 우선
        - 없으면 start_time 기준 가장 최근 항목 반환
        rL   r   Nr|   r   c                 ,    | d   j                  dd      S Nr   r   r   r8   xs    r#   r   z+TaskTimer.get_task_status.<locals>.<lambda>=  s    adhh|R.H r%   r   r   c                 ,    | d   j                  dd      S r   r   r   s    r#   r   z+TaskTimer.get_task_status.<locals>.<lambda>@  s    QqTXXlB-G r%   )r>   r   r   r8   r   )r?   r   r   r   r   r   r   s          r#   get_task_statuszTaskTimer.get_task_status'  s     dkk'**;;w'00 337;;w3G3M3M3Ojic4SVSaSabhSisDkj
j 1;^93dhhx>PT]>]C;^^w$HI!LL :#GHKK k
 _s   	B>#B>4CCr|   c                     g }| j                   d   j                         D ]!  \  }}|	|d   |k(  s|j                  |       # t        |      |dS )u   작업 목록 조회rL   r|   )totalrL   )r>   r   appendr   )r?   r|   rL   r   r   s        r#   
list_taskszTaskTimer.list_tasksB  s]     "&++g"6"<"<"> 	(GY~8!4!>Y'	( Ue44r%   project_filterc                    i }| j                   d   j                         D ]f  }|j                  dd      }|r|dk(  r|r||k7  r%||vr	dddd||<   ||   dxx   dz  cc<   |j                  d	      d
k(  sW||   d
xx   dz  cc<   h |j                         D ])  \  }}|d   dkD  rt	        |d
   |d   z  dz        nd|d<   + d|iS )u    프로젝트별 진행률 계산rL   rv   r   r,   r   )r   r   pctr   r   r|   r   d   r   projects)r>   valuesr8   r   round)r?   r   project_statsr   pidstatss         r#   calculate_progresszTaskTimer.calculate_progressM  s   35W-446 
	5I--b1C#/#"7-'/0q%Kc"#w'1,'}}X&+5c";/14/
	5 (--/ 	iJCOTU\~`aOa5{!3eGn!Ds!JKghE%L	iM**r%   c                 (   |r|dk(  ry| j                   dz  dz  }|j                         st        j                  d|        y| j	                  |      }|d   j                  |      }|sy	 t        |dd	
      5 }t        j                  |      }ddd       d}j                  dg       D ]M  }|j                  d      |k(  s|d   |d   |d   t        j                         j                         d|d<   d} n |rt        j                         j                         |d<   t        j                  d|j                  ddd	      5 }	t        j                  ||	dd       ddd       t!        j"                  	j$                  t'        |             t        j)                  d| d|d    d       yt        j+                  d|        y# 1 sw Y   =xY w# 1 sw Y   xxY w# t,        $ r"}
t        j/                  d|
        Y d}
~
yd}
~
ww xY w)u5   active-projects.json의 progress 필드 자동 갱신r,   Nr4   zactive-projects.jsonu   active-projects.json 없음: r   r   rG   rH   rI   Factiveidr   r   r   )r   r   r   
updated_atprogressTlast_updatedrF   r_   )modera   rb   rc   rJ   rd   re   u   active-projects.json 갱신:     → %u-   active-projects.json에 프로젝트 없음: u$   active-projects.json 갱신 실패: )r1   rM   rX   r   r   r8   rP   rT   rU   r   r   r   rh   rj   rN   rk   r6   ro   rp   rq   r   debugrW   rY   )r?   rv   active_projects_fileprogress_data
proj_statsr\   ap_dataupdatedprojectrs   r]   s              r#   r   z"TaskTimer._update_project_progress_  s   Z83#22X=@VV#**,NN:;O:PQR//z/J":.22:>
	E*C'B 'a))A,'G";;x4 	;;t$
2)%0%/%<!+G!4&.lln&>&>&@	+GJ' #G	 *2,,.*B*B*D'00,33 !$ J IIgsqIJ 

388S)=%>?;J<uZX]M^L__`abLZLYZ5' 'J J  	ELL?sCDD	EsV   .G& <G3G& BG& 	G#AG& 4G& GG& G#G& &	H/HHc                    |sy|j                  dd      }| j                  d   j                         D ]0  \  }}|j                  d      dk(  s|j                  d      |k(  s0 y | j                  dz  d	z  d
z  }	 |j                         r-t        |dd      5 }t        j                  |      }ddd       ndi i}dvri |d<   ddl	m
} t        j                  |j                        j                  d      }	d|	d|d   |<   ddl}
|j                  j!                  dd       |
j#                  d|j                  ddd      5 }t        j$                  ||dd       ddd       t'        j                   j(                  t+        |             y# 1 sw Y   xY w# 1 sw Y   ?xY w# t,        $ r%}t.        j1                  d| d|        Y d}~yd}~ww xY w)u   해당 팀의 bot-activity.json status를 idle로 갱신.

        단, 해당 팀에 아직 running 상태인 task가 있으면 갱신하지 않음.
        Nz-teamr   rL   r|   r   r&   r4   eventszbot-activity.jsonrG   rH   rI   botsr   )timezonez%Y-%m-%dT%H:%M:%SZidle)r|   sinceTrC   rF   Fr_   r`   rd   re   u!   bot-activity.json 갱신 실패 (): )ro   r>   r   r8   r1   rM   rP   rT   rU   r   r   r   utcr   rh   rN   rO   rj   rk   r6   rp   rq   rW   rX   rY   )r?   r&   bot_keyr   tdatabot_activity_pathr\   r   _tznow_utcrr   rs   r]   s                r#   r   zTaskTimer._update_bot_activity  s   
  //'2. ++g.446 	JCyy"i/EIIi4HG4S	
 !//(:XEH[[	N '')+S7C (q99Q<D( ( |T!!V0ll377+445IJG/5$HDL! )$$**4$*G--%,,  .  C 		$%BC JJsxx%6!783( ("C C  	NLL<WISLMM	NsI   <F* F0BF* F 1F* FF* F'#F* *	G3GGactionr   c           
         	 | j                   j                  j                  dd       | j                   j                         rt	        | j                   dd      5 }t        j                  |t
        j                         	 t        j                  |      }t        j                  |t
        j                         	 ddd       ni }dvrg |d<   d|vrd|d<   |j                  d	d
      }t        j                         j                         }|dk(  rf|d   D cg c]  }|j                  d	      |k7  s| c}|d<   |d   j!                  ||j                  dd
      dd|j                  d|      d       n0|dk(  r+|d   D cg c]  }|j                  d	      |k7  s| c}|d<   ||d<   | j                   j                  dz  }t	        |d      5 }	t        j                  |	t
        j"                         	 t%        j&                  d| j                   j                  ddd      5 }
t        j(                  ||
dd       |
j+                          t-        j.                  |
j1                                ddd       t-        j2                  
j4                  t7        | j                                t        j                  |	t
        j                         	 ddd       y# t        j                  $ r i }Y ]w xY w# t        j                  |t
        j                         w xY w# 1 sw Y   dxY wc c}w c c}w # 1 sw Y   xY w# t        j                  |	t
        j                         w xY w# 1 sw Y   yxY w# t8        $ r Y yw xY w)u   pipeline-status.json 업데이트.

        action="start": active_tasks에 작업 추가 (stage=2, stage_name="AI 작업 수행")
        action="end"  : active_tasks에서 task_id 제거
        TrC   rG   rH   rI   Nactive_taskslast_health_checkr   r   r   r&   rd   u   AI 작업 수행r   )r   teamstage
stage_name
started_atr   r   z.pipeline-status.lockrF   Fr_   r`   re   )r;   rN   rO   rM   rP   rQ   rR   rS   rT   rU   JSONDecodeErrorrV   r8   r   r   r   r   ri   rh   rj   rk   rl   r6   rm   rn   ro   rp   rq   rW   )r?   r   r   r\   pipeline_datar   now_isotrZ   r[   rs   s              r#   r   z!TaskTimer._update_pipeline_status  s
   C	%%,,224$2O ((//1$33S7K 6qKK5==16(,		! Au}}56 6 !# ]202n-"-75912mmIr2Glln..0G   -^<1i@PT[@[A1n- n-44#* )i <!"&8&/mmL'&J 5,^<1i@PT[@[A1n- -4M.)1188;RRIi% 8GU]]38!44 55<<$%!( 	/ 		-5QRS		.	/ JJsxxT-F-F)GHKK78 8O  // +(*+ Au}}56 6,11	/ 	/ KK78 8"  		s   AM8 %L K$L ;AM8 L-2L-6AM8 L2L2!0M8 %M,7.M%AL72;M-$M,M8 K40K73K44K77&LL  L*%M8 7M 	<M&M))M,,M51M8 5M8 8	NNr   r   c                    | j                   dz  dz  }|j                  dd       ||j                  dd      |j                         |d}d}|j                  d	      |d	   }|| d
z  }|S|j	                         rC	 t        |dd      5 }	t        j                  |	      }
ddd       
j                  d	      |
d	   }|[|| dz  }|j	                         rC	 t        |dd      5 }	t        j                  |	      }ddd       j                  d	      |d	   }|||d	<   t        j                  d|ddd      5 }t        j                  ||dd       ddd       t        j                  j                  t        |             y# 1 sw Y   xY w# t        $ r Y w xY w# 1 sw Y   xY w# t        $ r Y w xY w# 1 sw Y   ixY w)u&   완료 이벤트 파일 생성 (.done)r4   r   TrC   r&   r   )r   r&   r   r   Nr   z.donerG   rH   rI   z
.qc-resultrF   r_   F)r   ra   rc   rb   rJ   rd   re   )r1   rO   r8   r   rM   rP   rT   rU   rW   rh   rj   rk   r6   ro   rp   rq   )r?   r   r   r   r   
events_direventr   
event_filer\   r   qc_result_fileqc_datars   s                 r#   r   zTaskTimer._write_event_file  s   ((83h>
5  }}Y3 **, (	
 	 ==%1!+.I  WIU"33
!2!2!4*cG< ,#yy|H,<<,8 ( 5I
 'WIZ*@@N$$&ncGD /"&))A,/{{;/;$+K$8	  !*E+ ((
 	@ IIeSuQ?	@ 	

388S_-A, ,  / / ! 	@ 	@s`   >F F
"F F1 )F%?F1 >G 
FF 	F"!F"%F.*F1 1	F=<F= G	secondsc                     |dk  rt        |       dS |dk  r$t        |dz        }t        |dz        }| d| dS t        |dz        }t        |dz  dz        }| d| dS )u   소요 시간 포맷팅<   u   초  u   분 u   시간 u   분)int)r?   r  minutessecshourss        r#   r   zTaskTimer._format_duration?  s     R<'l^3''t^'B,'Gw|$DYd4&,,$'E7T>R/0GWGG9C00r%   datec                 t    |t        j                         }|j                  d      }| j                  | dz  S )u3   해당 날짜의 일일 로그 파일 경로 반환%Y-%m-%dz.md)r   r   r   r:   )r?   r  date_strs      r#   _get_daily_log_pathzTaskTimer._get_daily_log_pathM  s:     <<<>D==,!!xj$444r%   r   c                    | j                         }| j                  j                  dd       t        j                         }|j                  d      }d| d}|j                         r(t        |dd      5 }|j                         }d	d	d	       n|}j                  d|       s||z   }t        |d
d      5 }|j                  |dz          d	d	d	       y	# 1 sw Y   NxY w# 1 sw Y   y	xY w)u8   일일 로그에 항목 추가 (완료된 작업 섹션)TrC   r  # #    업무일지

## 완료된 작업
rG   rH   rI   Na
)r  r:   rO   r   r   r   rM   rP   readr   write)r?   r   log_pathcurrent_dater  headerr\   contents           r#   r   zTaskTimer._append_to_daily_logV  s     ++-   =||~((4hZEF??hg6 #!&&(# # G !!Bxj/2w&G (C'2 	&aGGI$%	& 	&# #	& 	&s   7C:C$C!$C-sectionc                    | j                         }| j                  j                  dd       t        j                         }|j                  d      }d| d}|j                         r|j                  d      }n|}|j                  d|       s||z   }d| }||v r|j                  d	      }	d
}
d
}t        |	      D ]9  \  }}|j                         |k(  r|}
|
|j                  d      s1||
kD  s7|} n |
I||	j                  ||       n|	j                  |       |j                  d	j                  |	      d       y
|j!                  d	      s|d	z  }|d	| d	| d	z  }|j                  |d       y
)uZ   일일 로그의 특정 섹션에 항목 추가. 섹션이 없으면 파일 끝에 생성.TrC   r  r  r  rH   rI   z## r  N)r  r:   rO   r   r   r   rM   	read_textr   split	enumeratestripinsertr   
write_textr   endswith)r?   r   r  r  r  r  base_headerr  section_headerlinessection_idxnext_section_idxilines                 r#   _append_to_sectionzTaskTimer._append_to_sectionn  s    ++-   =||~((48*$JK??(('(:G!G!!Bxj/2!G+GwiW$MM$'EK#$U+ 4::<>1"#K ,1GAP[O'($ &#/LL!19=LL+##DIIe$4w#G %tOGR'r)B77Gg6r%   c                    dddiddiddiddidi}| j                   j                         r	 | j                   j                  dz  }|j                  j                  dd       t	        |d      5 }t        j                  |t
        j                         	 t	        | j                   d	d
      5 }t        j                  |      cddd       t        j                  |t
        j                         cddd       S | j                  |       |S # 1 sw Y   nxY w	 t        j                  |t
        j                         n)# t        j                  |t
        j                         w xY w	 ddd       n# 1 sw Y   nxY w# t        $ rB}t        j                  d| j                    d|        | j                  |       |cY d}~S d}~ww xY w)u$   횡단조직 상태 데이터 로드cross_functionalr|   r   )r   r   r   r   .cross-functional-status.lockTrC   rF   rG   rH   rI   Nu#   횡단조직 파일 로드 실패: rK   )r<   rM   rN   rO   rP   rQ   rR   rS   rT   rU   rV   rW   rX   rY   _save_cross_status)r?   defaultrZ   r[   r\   r]   s         r#   _load_cross_statuszTaskTimer._load_cross_status  s    !6*"F+!6*"F+	!
 %%,,. 66==@__	  &&td&C)S) <WKK7<!$"<"<cGT 0XY#'99Q<0 0 GU]];< < 	(0 0 0 GU]];GU]];;< < <  B4C]C]B^^abcadef''0sr   AE> /%E1D>-D	D>$E1/	E> D	D>%E1>&E$$E1(	E> 1E:6E> >	G	7G>G	G	r   c                 B   ddl }	 | j                  j                  j                  dd       | j                  j                  dz  }t	        |d      5 }t        j                  |t
        j                         	 |j                  d| j                  j                  ddd	
      5 }t        j                  ||dd       ddd       t        j                  j                  t        | j                               t        j                  |t
        j                         	 ddd       y# 1 sw Y   jxY w# t        j                  |t
        j                         w xY w# 1 sw Y   yxY w# t         $ r/}t"        j%                  d| j                   d|        Y d}~yd}~ww xY w)u7   횡단조직 상태 데이터 저장 (원자적 쓰기)r   NTrC   r.  rF   Fr_   rH   r`   rd   re   u#   횡단조직 파일 저장 실패: rK   )rh   r<   rN   rO   rP   rQ   rR   ri   rj   rT   rk   r6   ro   rp   rq   rV   rW   rX   rY   )r?   r   rr   rZ   r[   rs   r]   s          r#   r/  zTaskTimer._save_cross_status  s\   $	c&&--33D43P2299<[[Ii% 8GU]]38"55 66==$%!( 6  K 		$%JK JJsxxT-G-G)HIKK78 8K K KK78 8  	cLL>t?Y?Y>ZZ]^_]`abb	csf   AE& %E8*D1"D%<;D17$EE& %D.	*D11&EEE#E& #E& &	F/%FF
agent_nameparent_teamc                    |t         vrEdj                  t        t                     }t        j	                  d| d| d       dd| d| dS t        |      s!t        j	                  d	|        dd
| ddS | j                         }t        j                         j                         }d||||d|d   |<   | j                  |       t        j                  d| d|        d||dS )u'   횡단조직 에이전트 소환 시작r   u   잘못된 cross agent: r~   r   rY   !Unknown cross-functional agent: ''. Allowed: r{   u(   잘못된 task_id 형식 (cross-start): ry   rz   r   )r|   r   ru   r   r4  r-  u   횡단조직 소환 시작: r   r   )r|   agentr   )ALLOWED_CROSS_AGENTSr   sortedrX   r   r$   r1  r   r   r   r/  r   )r?   r3  r   ru   r4  allowed
cross_datar   s           r#   cross_startzTaskTimer.cross_start  s   11ii'; <=GNN4ZLG9TUVW!=j\V]U^_ 
  (NNEgYOP!5gY>cd 
 ,,.
\\^--/
 &$&6

%&z2 	
+2:,eG9MN#jWMMr%   c                    |t         vrAdj                  t        t                     }t        j	                  d|        dd| d| dS | j                         }|d   j                  |i       }|j                  dd	      d	k(  rt        j                  d
|        d|dS dd	i|d   |<   | j                  |       t        j                  d|        d|dS )u'   횡단조직 에이전트 소환 종료r   u   잘못된 cross agent (end): rY   r6  r7  r{   r-  r|   r   u#   횡단조직 이미 idle (멱등): ended)r|   r8  u   횡단조직 소환 종료: )	r9  r   r:  rX   r   r1  r8   r   r/  )r?   r3  r;  r<  agent_states        r#   	cross_endzTaskTimer.cross_end  s    11ii'; <=GNN::,GH!=j\V]U^_ 
 ,,.
 !3488RH ??8V,6KK=j\JK%
;;6>5G
%&z2
+2:,?@!J77r%   running_hoursreserved_minutesc           
      p   t        j                         }|dz  }|dz  }g }| j                  d   j                         D ]   \  }}|j	                  d      }	|	}
d}|	dk(  rD|j	                  d      }|ry	 t        j
                  |      }||z
  j                         }||kD  rd}nH|	dk(  rC|j	                  d      }|r0	 t        j
                  |      }||z
  j                         }||kD  rd}|sd|d<   ||d<   |j                         |d<   |j                  ||
|d       t        j                  d| d|
 d| d        |r| j                          |S # t        $ r%}t        j                  d	| d
|        Y d}~d}~ww xY w# t        $ r%}t        j                  d| d
|        Y d}~d}~ww xY w)uM  stale 상태로 전환할 작업을 정리

        - running 상태이고 start_time이 running_hours 초과: stale_reason="timeout_running"
        - reserved 상태이고 reserved_at이 reserved_minutes 초과: stale_reason="timeout_reserved"

        반환값: 정리된 task 목록 (task_id, prev_status, stale_reason)
        r  r  rL   r|   Nr   r   timeout_runningu   start_time 파싱 실패 (r   reservedreserved_attimeout_reservedu   reserved_at 파싱 실패 (r   stale_reasonstale_atr   prev_statusrI  u   stale 전환: r   u    → stale, reason=r   )r   r   r>   r   r8   r   r   
ValueErrorrX   r   r   r   r   rt   )r?   rB  rC  r   running_thresholdreserved_thresholdcleanedr   r   r|   rL  rI  start_time_strr   elapsedr]   reserved_at_strrG  s                     r#   cleanup_stalezTaskTimer.cleanup_stale  s    lln)D0-2"&++g"6"<"<"> &	iGY]]8,F KL"!*|!<!U%-%;%;N%K
#&#3"B"B"D"%66+<L :%"+--">"V&.&<&<_&M#&#4"C"C"E"%77+=L &-	(#,8	.)(+	*%#*'2(4 nWIR}DWXdWeefghM&	iP = & U)CG9CPQs'STTU & V)DWISQRPS'TUUVs0   3/E</F	FE??F	F5F00F5message
entry_typec                 F   t        j                         }|r|nd}d|j                  d       d| d| }t        j	                  |      }t
        j                  d| d|dd         |r| j                  ||       n| j                  |       d	|||j                         d
S )u-  일일 로그에 직접 메모 추가.

        entry_type:
          note     - 완료된 작업 섹션 (기본값)
          decision - 의사결정 섹션
          system   - 시스템 변경 섹션
          arch     - 아키텍처 논의 섹션
          dispatch - 위임 기록 섹션
        r*   r   r   r   r   u   로그 추가: [N2   logged)r|   typerU  	timestamp)
r   r   r   ENTRY_TYPE_SECTIONr8   rX   r   r+  r   r   )r?   rU  rV  r   
type_labelr   r  s          r#   add_log_entryzTaskTimer.add_log_entryA  s     lln#-Z6
#,,z232j\G9M	$((4&zl"WSb\NCD##Iw7%%i0"J7Y\YfYfYhiir%   )Nr   N)r   r   r,   r   )r   )r   r   )       @      >@)r*   )#__name__
__module____qualname____doc__r   rq   r@   r   r=   rt   r   r   r   r   r   r   r   r   dictr   r   floatr   r   r   r  r   r+  r1  r/  r=  rA  listrT  r^   r%   r#   r0   r0   M   sa   (*x} * *!d !&U6 uw;_;_%(;_<?;_RU;_nq;_	;_z
3 
8C= 
2H
 H
 H
T H
TLs Lx~ L6	5# 	5$ 	5+# +$ +$)E3 )E4 )EV/NC /ND /NbIc Id It IV7. 7. 7. 7.]b 7.gk 7.r1 1# 15(: 5d 5&c &d &0,7C ,7# ,7$ ,7\D 8ct c c0Nc NC Nc N]` Njn N@8C 8D 8.85 8% 8[_ 8tjS jc jt jr%   r0   c                     t        t        j                        dk  rbt        d       t        d       t        d       t        d       t        d       t        d       t        d       t        j                  d	       t        j                  d	   } t               }| d
k(  rt        t        j                        dk  r t        d       t        j                  d	       t        j                  d   }t        |      s$t        d| d       t        j                  d	       d}d}d}d}t        j                  dd }d}|t        |      k  r||   dk(  r|d	z   t        |      k  r||d	z      }|dz  }n||   dk(  r|d	z   t        |      k  r||d	z      }|dz  }nb||   dk(  r|d	z   t        |      k  r||d	z      }|dz  }n;||   dk(  r|d	z   t        |      k  r||d	z      }|dz  }n|s||   }n|s||   }|d	z  }|t        |      k  r|j                  |||||      }	t        t        j                  |	dd             y| dk(  rt        t        j                        dk  r t        d       t        j                  d	       t        j                  d   }t        |      s$t        d| d       t        j                  d	       d}
t        j                  dd }d}|t        |      k  r;||   dk(  r|d	z   t        |      k  r||d	z      }
|dz  }n|d	z  }|t        |      k  r;|j                  ||
      }	t        t        j                  |	dd             y| dk(  rt        t        j                        dk  r t        d       t        j                  d	       t        j                  d   }t        |      s$t        d| d        t        j                  d	       |j                  |      }	|	r"t        t        j                  |	dd             yt        d!| d"| d#       t        j                  d	       y| d$k(  rd}t        j                  dd }d}|t        |      k  r]||   dk(  r|d	z   t        |      k  r||d	z      }|dz  }n't        d%||    d&       t        j                  d	       |t        |      k  r]|j                  |'      }	t        t        j                  |	dd             y| d(k(  rdt        t        j                        dkD  rt        j                  d   nd}|j                  |      }	t        t        j                  |	dd             y| d)k(  rit        t        j                        dk  r t        d*       t        j                  d	       d+}g }t        j                  dd }d}|t        |      k  rO||   d,k(  r|d	z   t        |      k  r||d	z      }|dz  }n|j                  ||          |d	z  }|t        |      k  rOd-j                  |      }|t         vrjd.j                  t         j#                               }t        d/| d0| d1t%        t         j#                               d           t        j                  d	       |j'                  ||      }	t        t        j                  |	dd             y| d2k(  r2d3}d4}d}t        j                  dd }d}|t        |      k  r||   d5k(  r)|d	z   t        |      k  r	 t)        ||d	z            }|dz  }nh||   d8k(  r)|d	z   t        |      k  r	 t)        ||d	z            }|dz  }n7||   d;k(  rd<}|d	z  }n't        d=||    d>       t        j                  d	       |t        |      k  r|rdd?lm} |j/                         }|d@z  }|dAz  }g }|j0                  dB   j3                         D ]  \  }}|j5                  d      }|dCk(  rT|j5                  dD      }|s0	 |j7                  |      }||z
  j9                         }||kD  r|j                  |dCdEdF       p|dGk(  sv|j5                  dH      }|s	 |j7                  |      }||z
  j9                         }||kD  r|j                  |dGdIdF        dJt        |      |dK}	n"|j;                  ||L      }dMt        |      |dN}	t        t        j                  |	dd             y| dOk(  rt        t        j                        dk  rBd.j                  t=        t>                    } t        dP|  dQ       t        j                  d	       t        j                  d   }!d}d}d}"t        j                  dd }d}|t        |      k  r||   dRk(  r|d	z   t        |      k  r||d	z      }|dz  }nu||   dk(  r|d	z   t        |      k  r||d	z      }|dz  }nN||   dk(  r|d	z   t        |      k  r||d	z      }"|dz  }n't        dS||    dT       t        j                  d	       |t        |      k  r|s t        dU       t        j                  d	       |jA                  |!|||"      }	t        t        j                  |	dd             y| dVk(  rt        t        j                        dk  rBd.j                  t=        t>                    } t        dP|  dW       t        j                  d	       t        j                  d   }!|jC                  |!      }	t        t        j                  |	dd             ydX}#t        dY|  dZ|# d[       t        j                  d	       y# t*        $ r. t        d6||d	z       d7       t        j                  d	       Y w xY w# t*        $ r. t        d9||d	z       d:       t        j                  d	       Y w xY w# t*        $ r Y w xY w# t*        $ r Y %w xY w)\u   CLI 인터페이스rd   zUsage:zF  python3 memory/task-timer.py start <task_id> [team_id] [description]z,  python3 memory/task-timer.py end <task_id>z/  python3 memory/task-timer.py status <task_id>z,  python3 memory/task-timer.py list [status]z,  python3 memory/task-timer.py log <message>z^  python3 memory/task-timer.py cleanup [--running-hours 2] [--reserved-minutes 30] [--dry-run]r   r      u   Error: task_id가 누락되었습니다. Usage: python3 memory/task-timer.py start <task_id> [--team <team_id>] [--desc <description>]. 예: python3 memory/task-timer.py start task-100.1 --team dev1-teamu!   Error: 잘못된 task_id 형식 'u   '. Expected 패턴: 'task-N[.N][_P.P][_x][+R]' (예: task-100, task-100.1, task-100_3.3). 올바른 형식으로 재실행하세요: python3 memory/task-timer.py start task-100.1r   r,   Nr   z--teamz--descz	--projectz--work-levelFre   r   u   Error: task_id가 누락되었습니다. Usage: python3 memory/task-timer.py end <task_id> [--qc-result PASS|FAIL|WARN]. 예: python3 memory/task-timer.py end task-100.1u   '. Expected 패턴: 'task-N[.N][_P.P][_x][+R]' (예: task-100, task-100.1, task-100_3.3). 올바른 형식으로 재실행하세요: python3 memory/task-timer.py end task-100.1z--qc-result)r   r|   u   Error: task_id가 누락되었습니다. Usage: python3 memory/task-timer.py status <task_id>. 예: python3 memory/task-timer.py status task-100.1u   '. Expected 패턴: 'task-N[.N][_P.P][_x][+R]' (예: task-100, task-100.1, task-100_3.3). 올바른 형식으로 재실행하세요: python3 memory/task-timer.py status task-100.1zError: Task 'u   '를 찾을 수 없습니다. 'python3 memory/task-timer.py list'로 등록된 task 목록을 확인하거나, 'python3 memory/task-timer.py start u$   '로 task를 먼저 시작하세요.r   u'   Error: 알 수 없는 progress 옵션 'uj   '. 유효한 옵션: --project <project_id>. 예: python3 memory/task-timer.py progress --project insuwikir   rh  logu   Error: 로그 메시지가 누락되었습니다. Usage: python3 memory/task-timer.py log <message> [--type note|decision|system|arch|dispatch]. 예: python3 memory/task-timer.py log '배포 완료' --type decisionr*   z--type r   u%   Error: 알 수 없는 로그 타입 'u   '. 유효한 타입: ua   . 올바른 타입으로 재실행하세요: python3 memory/task-timer.py log '<message>' --type cleanupr`  ra  z--running-hoursu   Error: --running-hours 값 'u   '이 유효한 숫자가 아닙니다. 숫자(float)를 입력하세요. 예: python3 memory/task-timer.py cleanup --running-hours 2.0z--reserved-minutesu   Error: --reserved-minutes 값 'u   '이 유효한 숫자가 아닙니다. 숫자(float)를 입력하세요. 예: python3 memory/task-timer.py cleanup --reserved-minutes 30.0z	--dry-runTu&   Error: 알 수 없는 cleanup 옵션 'u   '. 유효한 옵션: --running-hours <float>, --reserved-minutes <float>, --dry-run. 예: python3 memory/task-timer.py cleanup --running-hours 2 --reserved-minutes 30 --dry-runr   r  r  rL   r   r   rE  rK  rF  rG  rH  dry_run)r|   would_clean_countrL   )rB  rC  rP  )r|   cleaned_countrL   zcross-startuD   Error: agent_name이 누락되었습니다. 허용된 에이전트: u   . Usage: python3 memory/task-timer.py cross-start <agent_name> --task <task_id> [--desc <description>] [--team <team_id>]. 예: python3 memory/task-timer.py cross-start loki --task task-100.1z--tasku*   Error: 알 수 없는 cross-start 옵션 'u   '. 유효한 옵션: --task <task_id>, --desc <description>, --team <team_id>. 예: python3 memory/task-timer.py cross-start loki --task task-100.1 --team dev1-teamu   Error: --task <task_id>가 누락되었습니다. cross-start 명령은 반드시 --task 옵션이 필요합니다. 예: python3 memory/task-timer.py cross-start loki --task task-100.1z	cross-endun   . Usage: python3 memory/task-timer.py cross-end <agent_name>. 예: python3 memory/task-timer.py cross-end lokizHstart, end, status, list, progress, log, cleanup, cross-start, cross-endu   Error: 알 수 없는 명령 'u   '. 유효한 명령: uj   . Usage: python3 memory/task-timer.py <command> [args]. 예: python3 memory/task-timer.py start task-100.1)"r   sysargvprintexitr0   r$   r   rT   dumpsr   r   r   r   r   r   r\  keysrh  r^  rg  rM  r   r   r>   r   r8   r   r   rT  r:  r9  r=  rA  )$commandtimerr   r&   ru   rv   rw   argsr)  resultr   r   r|   rV  	msg_partsrU  validrB  rC  ro  _dtr   rN  rO  r   r   rQ  r   rR  rS  rG  rP  allowed_agentsr3  r4  valid_commandss$                                       r#   mainr  [  s5    388}qhVW<=?@<=<=nohhqkGKE 'sxx=1V
 HHQK((1+(3G9 =k l
 HHQK
 
xx|#d)mAw("q1us4y'8q1u+QaH$QT):"1q5kQaK'AECI,=!!a%[
QaN*q1us4y/@!!a%[
Q "1gG$"&q'KQ' #d)m* !!'7KZXdjjeA>?	E	sxx=1C
 HHQK((1+(3G9 =i j
 HHQK 	xx|#d)mAw-'AECI,= QK	QQ #d)m 9=djjeA>?	H	sxx=1F
 HHQK((1+(3G9 =l m
 HHQK&&w/$**V%BCy )77>i?ce
 HHQK	J	xx|#d)mAw+%!a%#d)*;!%a!eQ=d1gY GT U
  #d)m )))HdjjeA>?	F	 #CHH 1!t!!&)djjeA>?	E	sxx=1X
 HHQK 
	xx|#d)mAw("q1us4y'8!!a%[
Q  a)Q #d)m ((9%//II05578E7
| D%%*G ,rrv  xJ  xO  xO  xQ  sR  ST  sU  rVW
 HHQK$$Wj9djjeA>?	I	xx|#d)mAw++AD	0A $)$q1u+$6M Qa00QUSY5F ',T!a%['9$ QaK'Q<T!WI Fr s
 A #d)mD 0'')C - 4!1B!6J&+ll7&;&A&A&C !!""x0Y&%.]]<%@N%!),):):>)JJ'*Z'7&F&F&HG&):: * 1 13:7@8I%&!" z)&/mmM&BO&!*-*;*;O*LK'*['8&G&G&IG&);; * 1 13:7A8J%&!"3!!D !*JZdeF))Xh)iG )CLSZ[FdjjeA>?	M	!sxx=1!YYv.B'CDN++9*: ;WX HHQKXXa[
xx|#d)mAw("q1us4y'8q1u+QaH$QT):"1q5kQaH$QT):"1q5kQ@a	 Jl m
 ! #d)m$ W
 HHQK"":w[QdjjeA>?	K	sxx=1!YYv.B'CDN++9*: ;CD HHQKXXa[
,djjeA>? d,WI 6!!/ 0 1AB	
 	k "  6tAE{m DX Y
 HHQK  "  9$q1u+ G\ ]
 HHQK V  * ! !   * ! !sH   m; n5 >o/1>o?;3n21n253o,+o,/	o<;o<?	pp__main__r_  )"re  rQ   rT   r6   rerr  rh   r   pathlibr   typingr   r   r   utils.loggerr	   ImportErrorpathr!  rq   __file__rN   rb  rX   compiler!   r(   r9  r    r$   r)   r\  r0   r  ri  r%   r#   <module>r     s     	 	 
    & &('
 
H	"**OP " : 0c 0d 0
'c 'd '  ! Kj Kj\_D zF A%  (HHOOAs4>007789'(s   B ACC