
    (<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mZmZ ddlm	Z	 ddl
mZ  e	ej                  j                  d e	e      j                         j                   j                               Zedz  dz  d	z  Zedz  dz  d
z  Zh dZde	deeeef      fdZde	deeeef      ddfdZde	deeeef      ddfdZdeeeef      defdZdeeef   dedefdZ	 	 d%dee	   dee	   defdZ 	 d&dedee	   deeeef      fdZ!	 	 d%dedededee	   dee	   deeef   fdZ"dejF                  ddfd Z$dejF                  ddfd!Z%dejF                  ddfd"Z&d'd#Z'e(d$k(  r e'        yy)(u'  learnings-archiver.py - learnings.jsonl TTL 아카이브 + 스킬별 격리 필터 유틸리티.

learnings.jsonl에서 만료된 항목을 learnings-archive.jsonl로 이동하고,
스킬별 격리 필터로 활성 learnings를 반환하며,
새 learning을 추가하는 기능을 제공한다.
    N)datetime	timedelta)Path)OptionalWORKSPACE_ROOTmemoryzskill-learningzlearnings.jsonlzlearnings-archive.jsonl>   cross-modelself-reviewgithub-learnonline-expertchampion-battlejay-feedbackpathreturnc                    | j                         sg S | j                  d      j                         j                         }|sg S g }|D ]9  }|j                         }|s|j	                  t        j                  |             ; |S )ui   JSONL 파일을 읽어 dict 리스트로 반환한다. 파일이 없거나 비어있으면 빈 리스트.utf-8encoding)exists	read_textstrip
splitlinesappendjsonloads)r   linesentrieslinestrippeds        K/home/jay/workspace/.worktrees/task-2057-dev2/scripts/learnings_archiver.py_load_jsonlr!   "   sx    ;;=	NNGN,224??AE	*,G 1::<NN4::h/01 N    r   c                     | j                   j                  dd       | j                  dd      5 }|D ]+  }|j                  t	        j
                  |d      dz          - 	 d	d	d	       y	# 1 sw Y   y	xY w)
u8   dict 리스트를 JSONL 파일로 쓴다 (덮어쓰기).Tparentsexist_okwr   r   Fensure_ascii
Nparentmkdiropenwriter   dumpsr   r   fentrys       r    _write_jsonlr4   1   q    KKdT2	3	) BQ 	BEGGDJJu59D@A	BB B B   1A,,A5c                     | j                   j                  dd       | j                  dd      5 }|D ]+  }|j                  t	        j
                  |d      dz          - 	 d	d	d	       y	# 1 sw Y   y	xY w)
u/   dict 리스트를 JSONL 파일에 append한다.Tr$   ar   r   Fr(   r*   Nr+   r1   s       r    _append_jsonlr9   9   r5   r6   existing_entriesc           	      V   | D ch c]  }t        |j                  dd             }}d}|D ];  }t        |t               s|j                  d      s&	 t	        |dd       }||kD  r|}= d|dz   d}||vr|S t        t        j                               S c c}w # t
        $ r Y zw xY w)	uH   learn-NNN 형식의 id를 생성한다. 충돌 시 UUID를 사용한다.id r   zlearn-   N   03d)strget
isinstance
startswithint
ValueErroruuiduuid4)r:   eexisting_idsmax_numentry_idnum	candidates          r    _generate_idrO   A   s    2BCQCdB(CLCG  h$)<)<X)F(12,'=!G 1S)*I$tzz| D  s   "BB	B('B(r3   nowc                     | j                  d      dk(  ry| j                  d      }|yt        j                  t        |            }||k  S )u   항목이 만료되었는지 확인한다.

    jay-feedback 예외: source가 jay-feedback이면 TTL과 무관하게 영구 유지 (immutable).
    sourcer   F
expires_at)rB   r   fromisoformatrA   )r3   rP   rS   
expires_dts       r    _is_expiredrV   U   sN     yyn,<(J''J8Jr"   learnings_patharchive_pathc                 .   | | nt         }||nt        }t        |      }|syt        j                         }g }g }|D ]1  }t        ||      r|j                  |       !|j                  |       3 |syt        ||       t        ||       t        |      S )u  learnings.jsonl에서 만료된 항목을 learnings-archive.jsonl로 이동한다.

    jay-feedback 예외: source가 jay-feedback이고 expires_at이 null인 항목은 아카이브 안 함.
    append-only 보장: 원본에서 삭제만, archive에 추가만.

    Args:
        learnings_path: learnings.jsonl 경로 (기본값: LEARNINGS_PATH)
        archive_path: learnings-archive.jsonl 경로 (기본값: ARCHIVE_PATH)

    Returns:
        아카이브된 항목 수
    r   )
LEARNINGS_PATHARCHIVE_PATHr!   r   rP   rV   r   r9   r4   len)	rW   rX   lpathapathr   rP   activeexpiredr3   s	            r    archive_expiredra   d   s      -8NnE(4L,E% G
,,.C)+F*,G !uc"NN5!MM% 	!  %! w<r"   
skill_namec                     ||nt         }t        |      }|sg S t        j                         }g }|D ]5  }|j	                  d      | k7  rt        ||      r%|j                  |       7 |S )u  특정 스킬의 활성 learnings만 반환한다.

    활성 조건: expires_at > now 또는 expires_at == null.
    다른 스킬의 learning은 절대 포함하지 않는다 (크로스 오염 방지).

    Args:
        skill_name: 조회할 스킬 이름
        learnings_path: learnings.jsonl 경로 (기본값: LEARNINGS_PATH)

    Returns:
        해당 스킬의 활성 learning dict 리스트
    rb   )rZ   r!   r   rP   rB   rV   r   )rb   rW   r]   r   rP   resultr3   s          r    get_learningsre      su      -8NnE% G	
,,.C)+F 99\"j0uc"e Mr"   rR   learningc           
      n   |t         vr-t        d| ddj                  t        t                            ||nt        }t        |      }t        |      }t        j                         }|j                         }	|dk(  rd}
n|t        d      z   j                         }
|| |||	|
d}t        ||g       |S )	u0  새 learning을 learnings.jsonl에 추가한다.

    Args:
        skill_name: 스킬 이름
        source: 학습 출처 (유효값: self-review, cross-model, jay-feedback,
                online-expert, github-learn, champion-battle)
        learning: 학습 내용 텍스트
        learnings_path: learnings.jsonl 경로 (기본값: LEARNINGS_PATH)
        archive_path: learnings-archive.jsonl 경로 (기본값: ARCHIVE_PATH)

    Returns:
        추가된 learning entry dict

    Raises:
        ValueError: source가 유효하지 않은 경우
    zInvalid source: 'z'. Must be one of: z, Nr   <   )days)r<   rb   rR   rf   
created_atrS   )VALID_SOURCESrF   joinsortedrZ   r!   rO   r   rP   	isoformatr   r9   )rb   rR   rf   rW   rX   r]   r:   rL   rP   rj   rS   r3   s               r    add_learningro      s    . ]",VH4KDIIV\]jVkLlKmnoo,8NnE"5),-H
,,.CJ 
I2..99;
    #E %%!Lr"   argsc                 V    t               }|dk(  rt        d       yt        | d       y)u"   archive 서브커맨드 핸들러.r   u.   아카이브할 만료 항목이 없습니다.u'   개 항목을 아카이브했습니다.N)ra   print)rp   counts     r    _cmd_archivert      s*    Ez>?>?@r"   c                     | j                   }t        |      }|st        d| d       yt        d| dt        |       d       |D ]#  }t        t	        j
                  |dd             % y)	u   get 서브커맨드 핸들러.'u-   ' 스킬의 활성 learnings가 없습니다.Nu   ' 스킬의 활성 learnings (u   개):F   r)   indent)skillre   rr   r\   r   r0   )rp   rb   	learningsr3   s       r    _cmd_getr|      sn    jjJj)I*JKL	Aj\7I7Gu
MN ?djjU1=>?r"   c                 `   | j                   }| j                  }| j                  }	 t        |||      }t	        d|d           t	        t        j                  |dd             y
# t        $ r=}t	        d| t        j                         t        j                  d	       Y d
}~y
d
}~ww xY w)u   add 서브커맨드 핸들러.)rb   rR   rf   u   Learning 추가 완료: r<   Frw   rx   u   오류: )filer?   N)rz   rR   rf   ro   rr   r   r0   rF   sysstderrexit)rp   rb   rR   learning_textr3   rI   s         r    _cmd_addr      s    jjJ++FM
!"

 	(t67djjU1=> n3::.s   A A' '	B-03B((B-c                  t   t        j                  d      } | j                  dd      }|j                  dd       |j                  d	d
      }|j	                  ddd       |j                  dd      }|j	                  ddd       |j	                  ddd       |j	                  ddd       | j                         }|j                  dk(  rt        |       y|j                  d	k(  rt        |       y|j                  dk(  rt        |       y| j                          t        j                  d       y)u   CLI 진입점.uG   learnings.jsonl TTL 아카이브 + 스킬별 격리 필터 유틸리티)descriptioncommandu	   명령어)desthelparchiveu8   만료된 learnings를 learnings-archive.jsonl로 이동)r   rB   u(   특정 스킬의 활성 learnings 조회z--skillTu   조회할 스킬 이름)requiredr   addu   새 learning 추가u   스킬 이름z--sourceu_   학습 출처 (self-review|cross-model|jay-feedback|online-expert|github-learn|champion-battle)z
--learningu   학습 내용 텍스트r?   N)argparseArgumentParseradd_subparsers
add_parseradd_argument
parse_argsr   rt   r|   r   
print_helpr   r   )parser
subparsers
get_parserr   rp   s        r    mainr     sV   $$1z{F&&IK&HJ G   &&7 ' J &   &&" ' J   
 r  
 &   D||y T				r"   __main__)NN)N)r   N))__doc__r   r   osr   rG   r   r   pathlibr   typingr   environrB   __file__resolver,   r   rZ   r[   rk   listdictobjectr!   r4   r9   rA   rO   boolrV   rE   ra   re   ro   	Namespacert   r|   r   r   __name__ r"   r    <module>r      so     	 
  (   bjjnn%5tH~7M7M7O7V7V7]7]^_(*-==@QQ(+;;>WWd tD$89 Bt Bd4+?&@ BT BB BtD,@'A Bd B4VV^(<#= # (tFFN+ ( t   &*#'*TN*4.* 	*^ &*""TN" 
$vv~
"R &*#'222 2 TN	2
 4.2 
&&.2jAx)) Ad A	?8%% 	?$ 	?8%% $ $5p zF r"   