
    jF                        d Z ddlZddlZddlZddlZddlZddlmZ ddlmZ ddl	m
Z
 	 ddlmZ  ee      Z eej$                  j'                  dd            Zed	z  d
z  Zed	z  dz  Zed	z  dz  Z eej$                  j'                  d e ej2                         dz  dz  dz  d	z                    Zed
z  Zdedee   fdZdedee   fdZdededee   fdZdedefdZ 	 	 	 d%dddedededz  dedz  dedz  de!de"fdZ#	 	 d&dedz  dedz  dee"   fd Z$d'd!ededz  de!fd"Z%d'dedz  dee"   fd#Z&d'dededz  de"dz  fd$Z'y# e$ r ddlZ ej"                  e      ZY 2w xY w)(u   
Memory Check Confirmation Number System (MC)

매 task 위임 시 MC-XXXX를 발급하여 메모리 읽기 여부를 추적한다.
MEMORY.md의 ★ 항목과 관련 피드백 파일 목록을 로그에 기록한다.
    N)datetime)Path)Any)
get_loggerWORKSPACE_ROOTz/home/jay/workspacememoryz	MEMORY.mdzmemory-check-log.jsonztask-timers.jsonMEMORY_CHECK_FEEDBACK_DIRz.claudeprojectsz%-home-jay--cokacdir-workspace-autosetmemory_pathreturnc                    	 | j                         sg S | j                  d      }g }	 |j                  d      D ]!  }d|vr	|j                         }t        j                  dd	|      }t        j                  d
d|      }t        j                  dd|      }t        j                  dd|      }t        j                  dd|      }t        j                  dd|      }t        j                  dd|      }t        j                  dd	|      }t        j                  dd	|      }t        j                  dd|      j                         }|s|j                  |       $ 	 |S # t        $ r'}t        j	                  d|  d|        g cY d}~S d}~ww xY w# t        $ r'}t        j	                  d|  d|        g cY d}~S d}~ww xY w)u$  memory_path 파일에서 ★ 포함 라인을 추출하고 마크다운 서식을 제거한다.

    Args:
        memory_path: 읽을 MEMORY.md 파일 경로

    Returns:
        ★ 항목 문자열 리스트 (마크다운 서식 제거됨). 파일 없거나 에러 시 빈 리스트.
    utf-8encodingu)   [parse_star_items] 파일 읽기 실패 (): N
u   ★z
^#{1,6}\s* z\*{1,3}([^*]*)\*{1,3}z\1z_{1,3}([^_]*)_{1,3}z~~([^~]*)~~z	`([^`]*)`z\[([^\]]*)\]\([^)]*\)z!\[([^\]]*)\]\([^)]*\)z	^[-*+]\s+z	^\d+\.\s+z\s+ u&   [parse_star_items] 파싱 중 에러 ()
exists	read_text	Exceptionloggerdebugsplitstripresubappend)r   contenteresultslinecleaneds         C/home/jay/workspace/.worktrees/task-2644-dev1/utils/memory_check.pyparse_star_itemsr%   *   s   !!#I'''9
 GMM$' 	(DD jjlGff]B8Gff5ugFGff3UGDGff^UG<Gff\5':Gff5ugFGff6wGGff\2w7Gff\2w7GffVS'288:Gw'3	(< NI  @SQRPSTU	@  =k]#aSQR	sF   E" E" D F F "	F+FFF	GG :G Gfeedback_dirc                     	 | j                         sg S t        d | j                  d      D              S # t        $ r'}t        j                  d|  d|        g cY d}~S d}~ww xY w)u$  feedback_dir에서 feedback_*.md 패턴에 맞는 파일명 리스트를 반환한다.

    Args:
        feedback_dir: 피드백 파일이 있는 디렉토리 경로

    Returns:
        파일명 리스트 (경로 없이 파일명만). 디렉토리 없거나 에러 시 빈 리스트.
    c              3   4   K   | ]  }|j                     y wN)name).0ps     r$   	<genexpr>z&find_feedback_files.<locals>.<genexpr>j   s     IaffIs   zfeedback_*.mdu2   [find_feedback_files] 디렉토리 읽기 실패 (r   N)r   sortedglobr   r   r   )r&   r    s     r$   find_feedback_filesr0   ^   sf    ""$IIl&7&7&HIII I,WZ[\Z]^_	s   5  5 	A%A A% A%	task_descc           	      D   	 t        |      }|sg S | j                         g }|D ]  }|}|j                  d      r|t        d      d }|j	                  d      r|dd }|j                  d      }|D cg c]-  }|st        j                  d|t        j                        r,|/ }}|st        fd|D              }	t        |      dk  rdnd	}
|	|
k\  s|j                  |        |S c c}w # t        $ r$}t        j                  d
|        g cY d}~S d}~ww xY w)u  task_desc 내용에 키워드 매칭되는 피드백 파일 목록을 반환한다.

    파일명에서 키워드를 추출하여 task_desc와 2개 이상 매칭 시 포함.
    키워드가 1개인 짧은 파일명은 1개 매칭으로도 포함.

    Args:
        task_desc: 작업 설명 문자열
        feedback_dir: 피드백 파일 디렉토리 경로

    Returns:
        매칭된 피드백 파일명 리스트
    	feedback_Nz.md_z^v?\d+$c              3   ,   K   | ]  }|v sd   yw)   N )r+   kw
task_lowers     r$   r-   z)match_feedback_to_task.<locals>.<genexpr>   s     GBbJ6FaGs   	r7      u,   [match_feedback_to_task] 매칭 중 에러: )r0   lower
startswithlenendswithr   r   match
IGNORECASEsumr   r   r   r   )r1   r&   feedback_filesmatchedfnamestempartsr,   keywordsmatch_count	thresholdr    r:   s               @r$   match_feedback_to_taskrK   p   s#   ",\:I__&
# 	&ED{+C,./}}U#CRy JJsOE#(]aAbhhz1bmm6\]H] G(GGK !]a/QIi'u%+	&.  ^  CA3GH	sG   C2 A#C2 5C-=%C-#C-'0C2 C2 -C2 2	D;DDDlog_pathc                    	 | j                         syt        | dd      5 }t        j                  |      }ddd       j	                  dg       }|sy|d   j	                  dd	      }t        j                  d
|      }|syt        |j                  d            dz   }|dk  rd|dS d| S # 1 sw Y   yxY w# t        $ r"}t        j                  d|        Y d}~yd}~ww xY w)uI  log_path의 JSON 파일에서 다음 MC ID를 계산하여 반환한다.

    checks 배열의 마지막 mc_id에서 숫자를 추출해 +1 한다.

    Args:
        log_path: memory-check-log.json 파일 경로

    Returns:
        "MC-XXXX" 형식의 ID 문자열 (제로패딩 4자리, 9999 초과 시 그대로 증가)
    zMC-0001rr   r   Nchecksmc_idzMC-0000zMC-(\d+)r7   i'  zMC-04du&   [get_next_mc_id] MC ID 계산 실패: )r   openjsonloadgetr   searchintgroupr   r   r   )rL   fdatarO   
last_mc_idr@   next_numr    s           r$   get_next_mc_idr^      s     (C'2 	 a99Q<D	  (B'BZ^^GY7
		+z2u{{1~&*t#''
###	  	 $  =aSABsD   B; B; B/B; -B; 'B; *B; /B84B; ;	C&C!!C&F)_skip_anu_memorytask_idr_   c                	   |du}|t         }|t        }|t        }|j                  dz  }d}	 |j                  j	                  dd       t        |d      }t        j                  |t        j                         t        |      }	g }
|s%|s#t        j                         rt        t              }
t               }g }|	|
z   D ])  }||vs|j                  |       |j                  |       + t        ||      }t!        |      }t#        j$                         j'                         }|j                         r.	 t        |dd      5 }t)        j*                  |      }ddd       nd	g i}d	vrg |d	<   ||z   }g }|	D ]X  }|dd
 }d}t1        j2                  ||z   j5                  d            j7                         dd }|j                  ||d|dd       Z |
D ]X  }|dd
 }d}t1        j2                  ||z   j5                  d            j7                         dd }|j                  ||d|dd       Z |D ][  }|dd
 }d| }t1        j2                  ||z   j5                  d            j7                         dd }|j                  ||d|dd       ] || |t9        |      |ddd|d	}|r||d<   |d	   j                  |       t        |dd      5 }t)        j:                  ||dd       ddd       t<        j?                  d| d|  dt9        |       dt9        |       d	       || |t9        |      |dd|d|6	 t        j                  |t        j@                         |jC                          S S # 1 sw Y   xY w# t(        j,                  t.        f$ r d	g i}Y 7w xY w# 1 sw Y   xY w# tD        $ r Y S w xY w# tD        $ rk}t<        jG                  d|        i cY d}~|E	 t        j                  |t        j@                         |jC                          S # tD        $ r Y S w xY wS d}~ww xY w# |E	 t        j                  |t        j@                         |jC                          w # tD        $ r Y w w xY ww xY w)u  MC(Memory Check Confirmation Number)를 발급하고 로그에 기록한다.

    워크스페이스 MEMORY.md와 Anu MEMORY.md에서 ★ 항목을 파싱하고,
    관련 피드백 파일을 매칭하여 로그에 기록한다.

    Args:
        task_id: 태스크 ID (예: "task-1454.1")
        task_desc: 작업 설명
        log_path: 로그 파일 경로 (기본: _DEFAULT_LOG_PATH)
        memory_path: 워크스페이스 MEMORY.md 경로 (기본: _DEFAULT_MEMORY_PATH)
        feedback_dir: 피드백 파일 디렉토리 (기본: _ANU_MEMORY_DIR)

    Returns:
        {"mc_id": "MC-XXXX", "task_id": "...", "timestamp": "...",
         "star_items_checked": N, "memory_items_read": [...]}
        에러 시 빈 dict 반환
    N.memory-check-log.lockT)parentsexist_okwrN   r   r   rO   d   zmemory/MEMORY.md   	star_item)memory_item_idsource_path	item_typecontent_previewack_requiredzanu_memory/MEMORY.mdzanu_memory/feedback_fileF)	rQ   r`   	timestampstar_items_checkedmemory_items_readpendingacknowledgedacked_atmemory_items_structuredmatched_feedback_filesr;   ensure_asciiindentz[issue_mc] u    발급: task_id=u   , ★항목=u   개, 피드백=u   개)rQ   r`   ro   rp   rq   rr   rs   ru   u&   [issue_mc] MC 발급 실패 (무시): )$_DEFAULT_LOG_PATH_DEFAULT_MEMORY_PATH_ANU_MEMORY_DIRparentmkdirrS   fcntlflockLOCK_EXr%   _ANU_MEMORY_PATHr   setaddr   rK   r^   r   now	isoformatrT   rU   JSONDecodeErrorOSErrorhashlibsha256encode	hexdigestr>   dumpr   r   LOCK_UNcloser   warning)r`   r1   rL   r   r&   r_   _custom_memory	lock_pathlock_fdws_star_itemsanu_star_itemsseenall_star_itemsitemmatched_feedbackrQ   ro   rZ   log_datarq   ru   rl   rj   	item_hashrE   entryr    s                              r$   issue_mcr      s   4 !,N$*&"::IGKdT:y#&GU]]+ )5 %'&6;K;R;R;T-.>?N $&!N2 	,D4%%d+	, 2)\J x( LLN,,.	??*(C': ,a#yy|H,
 !"~H8#!#HX*-== 9; " 	D"4CjO,Ko(E'M'Mg'VWaacdgeghI#**&/#.!,'6$(		 # 	D"4CjO0Ko(E'M'Mg'VWaacdgeghI#**&/#.!,'6$(		 & 	E#DSkO'w/Ko(E'M'Mg'VWaacdgeghI#**&/#.!0'6$(		 ""%n"5!2!'>!
 .>E*+!!%((C'2 	AaIIha@	A 	% 1' ;^,- .-./s4	
 ""%n"5!2!'>	
 GU]]3 K, ,(('2 *$b>*V	A 	A8    ?sCD	GU]]3 	 	 GU]]3 	 s   BN> 	A-N> 7M? M2M? #E-N> N#*AN> <4N/2M<7M? ?N N> N  N> #N,(N> /	N;:N;>	P2P- P2!P5 (4P	P)(P)-P22P5 5Q>94Q.-Q>.	Q:7Q>9Q::Q>timers_pathc                    | t         } |t        }	 g }|j                         rt        |dd      5 }t	        j
                  |      }ddd       j                  di       j                         D ]>  \  }}|j                  d      dk(  s|j                  ||j                  dd	      d
       @ |sg S t               }| j                         rj	 t        | dd      5 }t	        j
                  |      }ddd       j                  dg       D ]'  }	|	j                  d      }
|
s|j                  |
       ) 	 |D cg c]  }|d   |vs| c}S # 1 sw Y   xY w# 1 sw Y   kxY w# t        j                  t        f$ r"}t        j                  d|        Y d}~gd}~ww xY wc c}w # t        $ r$}t        j                  d|        g cY d}~S d}~ww xY w)ur  running 상태이지만 MC가 발급되지 않은 태스크 목록을 반환한다.

    Args:
        log_path: memory-check-log.json 경로 (기본: _DEFAULT_LOG_PATH)
        timers_path: task-timers.json 경로 (기본: _DEFAULT_TIMERS_PATH)

    Returns:
        [{"task_id": "task-1454.1", "team_id": "dev1-team"}, ...]
        에러 시 빈 리스트 반환
    NrN   r   r   tasksstatusrunningteam_idr   )r`   r   rO   r`   u3   [get_unchecked_tasks] 로그 파일 읽기 실패: u%   [get_unchecked_tasks] 조회 실패: )rz   _DEFAULT_TIMERS_PATHr   rS   rT   rU   rV   itemsr   r   r   r   r   r   r   r   )rL   r   running_tasksrZ   timers_datar`   
task_entrychecked_task_idsr   checktidr    ts                r$   get_unchecked_tasksr   {  s    $*$$&k39 +Q"iil+'2w'C'I'I'K #>>(+y8!(('.'1~~i'D I &)U??X(C': ,a#yy|H,%\\(B7 2E))I.C(,,S12 )QaAiL@P,PQQ9+ +$, , (('2 XRSTRUVWWX R <QC@A	s    F- EAF- )F- 5F- E* E40E* %E* 8F- =F(
F(F- EF- E'#E* *F%F F-  F%%F- -	G6GGGrQ   c                    |t         }|j                  dz  }d}	 t        |d      }t        j                  |t        j
                         |j                         s:	 |6	 t        j                  |t        j                         |j                          yyt        |dd      5 }t        j                  |      }ddd       j                  dg       D ]  }|j                  d	      | k(  sd|d
<   d|d<   t        j                         j                         |d<   t        |dd      5 }t        j                   ||dd       ddd        |6	 t        j                  |t        j                         |j                          yy 	 |6	 t        j                  |t        j                         |j                          yy# t        $ r Y yw xY w# 1 sw Y   xY w# 1 sw Y   xY w# t        $ r Y yw xY w# t        $ r Y yw xY w# t        $ ri}t"        j%                  d|        Y d}~|E	 t        j                  |t        j                         |j                          y# t        $ r Y yw xY wyd}~ww xY w# |E	 t        j                  |t        j                         |j                          w # t        $ r Y w w xY ww xY w)u!  MC를 ack 처리한다. pending=False, acknowledged=True, acked_at=now 설정.

    Args:
        mc_id: ack 대상 MC ID (예: "MC-0042")
        log_path: 로그 파일 경로 (기본: _DEFAULT_LOG_PATH)

    Returns:
        성공 시 True, 실패 또는 mc_id 미발견 시 False
    Nrb   re   FrN   r   r   rO   rQ   rr   Trs   rt   r;   rw   u   [ack_mc] 실패: )rz   r}   rS   r   r   r   r   r   r   r   rT   rU   rV   r   r   r   r   r   r   )rQ   rL   r   r   rZ   r[   r   r    s           r$   ack_mcr     s    $"::IGy#&GU]]+ " GU]]3 ! (C'2 	 a99Q<D	  XXh+ 	Eyy!U*#(i (,n%$,LLN$<$<$>j!(C': EaIIdAE!DE GU]]3 	 
 GU]]3   )	  	 E E  9   *1#./GU]]3 	 	 GU]]3 	 s   A H 4F? H #G91H +=H (G	H 4G' H 	4G6 ?	G
GGH G$	 H '	G32G36	HH	I7I2&I: -4I" "	I.-I.2I77I: :K>4J32K3	J?<K>J??Kc                 >   | t         } | j                         sg S 	 t        | dd      5 }t        j                  |      }ddd       j                  dg       D cg c]  }|j                  dd      du s| c}S # 1 sw Y   >xY wc c}w # t        $ r g cY S w xY w)	u   ack되지 않은 MC entries 반환.

    Returns:
        [{"mc_id": "...", "task_id": "...", "timestamp": "...", ...}, ...]
    NrN   r   r   rO   rr   FTrz   r   rS   rT   rU   rV   r   )rL   rZ   r[   r    s       r$   get_pending_mcsr     s     $??	(C'2 	 a99Q<D	 88Hb1UaQUU9e5LPT5TUU	  	 U 	s:   B A= B B	6B	:B =BB BBc                 H   |t         }|j                         sy	 t        |dd      5 }t        j                  |      }ddd       j                  dg       D cg c]  }|j                  d      | k(  s| }}|r|d   S dS # 1 sw Y   FxY wc c}w # t        $ r Y yw xY w)ut   task_id에 해당하는 가장 최근 MC entry 반환.

    Returns:
        entry dict 또는 None (미발견)
    NrN   r   r   rO   r`   rP   r   )r`   rL   rZ   r[   r    matchess         r$   get_mc_by_taskr     s     $??(C'2 	 a99Q<D	 "hhx4Ti8HG8S1TT%wr{/4/	  	 T s?   B BB B5B9B B B	B 	B! B!)NNN)NNr)   )(__doc__r   r   rT   osr   r   pathlibr   typingr   utils.loggerr   __name__r   ImportErrorlogging	getLoggerenvironrV   
_WORKSPACEr{   rz   r   strhomer|   r   listr%   r0   rK   r^   booldictr   r   r   r   r   r8       r$   <module>r      sb      	 	   )'!F "**..!13HIJ
!H,{: ),CC !H,/AA  JJNN#IDIIK)#j03ZZ]eef #[0 1$ 149 1hd tCy $/c / /$s) /d#T #c #R !# $p #ppp Tkp 	p
 +p p 
ph !#7Tk77 
$Z7t*# * * *ZdTk T$Z $C 4$; $+   )Wx(F)s   E E+*E+