
    Ri              
       l   d 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	m
Z
 ej                  j                  d e ee      j                         j                   j                               Z ee      dz  Z ed      Z ed	      Z ed
      Z ed      Z ee      dz  dz  Z	  eed      5 Z ej4                  e      Zddd        eej                  di       j                  dd            Zefdede ee
f   fdZ!efdede ee
f   fdZ"efdede#e   fdZ$dededz  fdZ%de ee
f   de ee
f   de#e   de ee
f   fdZ&dedefdZ'efd e ee
f   deddfd!Z(efdeddfd"Z)d%d#Z*e+d$k(  r e*         ejX                  d       yy# 1 sw Y   xY w# eej:                  f$ r i ZY w xY w)&u  
whisper-save-guidance.py

세션 종료 시 현재 상태를 스냅샷하여 다음 세션 가이던스를 생성하는 스크립트.

입력:
  - /home/jay/workspace/memory/events/bot-activity.json
  - /home/jay/workspace/memory/task-timers.json
  - /home/jay/workspace/memory/events/*.done

출력:
  - /home/jay/workspace/memory/whisper/session-guidance.json

사용법:
  python3 whisper-save-guidance.py
    N)datetime	timedeltatimezone)Path)AnyWORKSPACE_ROOTmemoryzevents/bot-activity.jsonztask-timers.jsoneventszwhisper/session-guidance.jsonconfigzconstants.jsonutf-8encoding
thresholds
idle_hours   base_dirreturnc                     | t         z  }	 t        j                  |j                  d            }|j	                  d      }t        |t              si S |S # t        t        j                  t        f$ r i cY S w xY w)u   bot-activity.json을 읽어 bots dict 반환.

    파일이 없거나 손상된 경우 빈 dict 반환 (graceful fallback).
    r   r   bots)
_BOT_ACTIVITY_RELjsonloads	read_textget
isinstancedictFileNotFoundErrorJSONDecodeErrorOSError)r   pathdatar   s       N/home/jay/workspace/.worktrees/task-2117-dev1/scripts/whisper-save-guidance.pyload_bot_activityr#   5   sp    
 ''Dzz$..'.:;xx$%It33W= 	   AA A  A87A8c                     | t         z  }	 t        j                  |j                  d            }|j	                  d      }t        |t              si S |S # t        t        j                  t        f$ r i cY S w xY w)u   task-timers.json을 읽어 tasks dict 반환.

    파일이 없거나 손상된 경우 빈 dict 반환 (graceful fallback).
    r   r   tasks)
_TASK_TIMERS_RELr   r   r   r   r   r   r   r   r   )r   r    r!   r&   s       r"   load_task_timersr(   E   sp    
 &&Dzz$..'.:;!%&It33W= 	r$   c                     | t         z  }|j                         sg S g }|j                         D ]-  }|j                  dk(  s|j	                  |j
                         / |S )u   events/ 디렉토리에서 *.done 파일을 스캔하여 task_id 목록 반환.

    확장자가 정확히 .done 인 파일만 포함 (.done.clear 등은 제외).
    z.done)_EVENTS_DIR_RELis_diriterdirsuffixappendstem)r   
events_dirtask_idsps       r"   scan_done_filesr3   U   s_    
 O+J	H! $88wOOAFF#$ O    tsc                     	 t        j                  |       }|j                   |j                  t        j
                        }|S # t        t        f$ r Y yw xY w)u:   ISO8601 문자열을 timezone-aware datetime으로 파싱.N)tzinfo)r   fromisoformatr7   replacer   utc
ValueError	TypeError)r5   dts     r"   
_parse_isor>   k   sQ    ##B'998<<0B		" s   AA AAr   r&   done_task_idsc                    t        j                  t        j                        }|j	                         D cg c]-  \  }}t        |t              s|j                  d      dk(  s,|/ }}}g }| j	                         D ]  \  }}t        |t              s|j                  d      dk7  r,|j                  dd      }	t        |	      }
|
L||
z
  j                         dz  }|t        k\  sl|j                  | d        |D cg c]  }| d	 }}g }|rd	j                  |      }g }|D ]n  }|j                  |i       }t        |t              r|j                  d
d      nd}t        |      }|r|j                  | d| d       ^|j                  |       p |j                  dd	j                  |              |r%d	j                  |      }|j                  d|        |r%d	j                  |      }|j                  d|        dj                  |      |rdndz   }|j                  d      ||||dS c c}}w c c}w )u7   수집된 데이터로 session-guidance dict를 생성.statusrunningidlesince g      @z-teamu    완료 확인 필요z, team_id()u   활성 작업: u   완료 미처리: u   유휴 팀: z. .u   대기 중.z%Y-%m-%dT%H:%M:%S)last_sessionguidancepending_dispatches
idle_teamsactive_tasks)r   nowr   r:   itemsr   r   r   r>   total_seconds_IDLE_THRESHOLD_HOURSr.   join_team_labelstrftime)r   r&   r?   rO   tidinforN   rM   bot_id	since_strsince_dtr   rL   parts	task_listtask_display_partsrF   
team_label	done_list	idle_listguidance_texts                        r"   generate_guidancerb   w   sP    ,,x||
$C #[[]Tjt.DRZI[_hIhL 
 J

 0$%88H'HHWb)	i(Hn335>
.../0 O\$\su,A%B$\$\ EIIl+	(* 	/C99S"%D6@t6L488Ir2RTG$W-J"))SE:,a*@A"))#.	/ 	tyy1C'D&EFGIIm,	))56IIj)	|I;/0IIe$u-HM %89!0 $ e( %]s   H<H<%H<IrF   c                 H    | syt        dd      D ]  }d| | v s| dc S  y)uy   team_id를 사람이 읽기 쉬운 팀 레이블로 변환.

    예: "dev1-team" → "1팀", "dev2-team" → "2팀"
    rE      
   devu   팀)range)rF   is     r"   rT   rT      s>    
 1b\ 9S9 r4   r!   c                     |t         z  }|j                  j                  dd       |j                  t	        j
                  | dd      d       y)	u@   session-guidance.json으로 저장 (기존 파일 덮어쓰기).T)parentsexist_okF   )ensure_asciiindentr   r   N)_GUIDANCE_RELparentmkdir
write_textr   dumps)r!   r   out_paths      r"   save_guidanceru      sK    
 -'HOO$6

4eA6  r4   c                     t        |       }t        |       }t        |       }t        |||      }t	        ||        y)u   전체 파이프라인 실행.)r   )r   r&   r?   N)r#   r(   r3   rb   ru   )r   r   r&   r?   guidance_datas        r"   runrx      s?    h/Dh/E#X6M%#M -(3r4   c                      t                y)u1   CLI 진입점. 인자 없음. exit code 항상 0.N)rx    r4   r"   mainr{      s    Er4   __main__)r   N)-__doc__r   ossysr   r   r   pathlibr   typingr   environr   str__file__resolverp   _WORKSPACE_ROOT_DEFAULT_BASE_DIRr   r'   r*   ro   _CONSTANTS_PATHopen_fload
_CONSTANTSr   r   floatrR   r   r#   r(   listr3   r>   rb   rT   ru   rx   r{   __name__exitrz   r4   r"   <module>r      s^  "  	 
 2 2   **..!13tH~7M7M7O7V7V7]7]3^_)H4 34 *+ x.45 '(25EE	o	0 #BTYYr]
#
 jnn\2>BB<QRST  (9  T#s(^   '8 t DcN   &7 d 49 ,	3 	8d? 	A
sCx.AS>A 9A 
#s(^	AH  * '

sCx.

 

$ + 4$ 4t 4

 zFCHHQK Y# #4//0 Js*   )
F 3FF FF F32F3