
    (<i                     :   U d 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
mZmZ ej                  j                  d e e	e      j#                         j$                  j$                              Z e	e      dz  Z e	e      dz  d	z  Z	  eed
      5 Z ej0                  e      Zddd       ej                  di       j                  dd      Zej                  di       j                  dd      Z	 ddlZeZeej@                  vrej@                  jC                  de       ddl"m#Z$ ddl"m%Z& ddl"m'Z(  e&       Z)e)jU                         D  ci c]  \  } }| jW                  dd      | c}} Z,e-eef   e.d<   de,d<    e(       Z/e0e   e.d<    e$       Z1e-eef   e.d<   g d)Z3e0e   e.d*<    e	d+      Z4dZ5efd,e	d-e-eef   fd.Z6efd,e	d-e-eef   fd/Z7efd,e	d-e0e-eef      fd0Z8efd1ed,e	d-ee-eef      fd2Z9efd,e	d-e-eef   fd3Z:defd4ee   d,e	d-e-eef   fd5Z;efd,e	d-e0e-eef      fd6Z<efd,e	d-e0e   fd7Z=efd,e	d-e0e-eef      fd8Z>efd,e	d-e0e-eef      fd9Z?efd,e	d-e0e-eef      fd:Z@e4fd;e	d-e0e-eef      fd<ZAefd,e	d-eBfd=ZCd>e-eef   d-e0e-eef      fd?ZDd@e-eef   d-e0e-eef      fdAZEdee4fd4ee   d,e	d;e	d-eFee-eef   f   fdBZGd@e-eef   dCe-eef   d-e-ee-eef   f   fdDZHdEed-efdFZIdGed-eeB   fdHZJdIe	d-efdJZKdTdKedLeBd-efdMZLdUdNedLeBd-efdOZMefdPe-eef   d,e	d-dfdQZNdVdRZOePdSk(  r eO        yy# 1 sw Y   xY w# eej6                  f$ r i ZY w xY wc c}} w # e2$ r dddddZ,g dZ/d d!d"d#d$d%d&d'd(Z1Y w xY w)Wu  
whisper-compile.py

bot-activity.json, task-timers.json, .done 파일, 보고서, session-guidance.json,
프로젝트 context, 질문 파일을 읽어서 XML 포맷 브리핑을 stdout으로 출력.

사용법:
    python3 whisper-compile.py [CWD]

exit code 항상 0.
    N)datetime	timedeltatimezone)Path)AnyOptionalWORKSPACE_ROOTmemoryconfigzconstants.jsonutf-8encoding
thresholds
idle_hours   ghost_hours   )build_bot_to_key_map)build_short_labels)get_dev_short_ids-team TEAM_NAME_MAPu   아누anu_DEV_SHORT_IDS_BOT_TO_DEVu   1팀u   2팀u   3팀)dev1dev2dev3r   )r   r   r   dev4dev5dev6dev7dev8r   r   r   r    r!   r"   r#   r$   )zbot-bzbot-czbot-dzbot-ezbot-fzbot-gzbot-hzbot-i)insuwiki
threadautoz
dev-systemPROJECT_KEYWORDSzG/home/jay/.claude/projects/-home-jay--cokacdir-workspace-autoset/memorybase_dirreturnc                     | dz  dz  }	 t        j                  |j                  d            }|j                  di       S # t        $ r i cY S w xY w)u=   bot-activity.json 로드 → {bot_id: {status, since}} 반환eventszbot-activity.jsonr   r   bots)jsonloads	read_textget	Exceptionr(   pathdatas      H/home/jay/workspace/.worktrees/task-2057-dev2/scripts/whisper-compile.pyload_bot_activityr6   T   sU    h!44Dzz$..'.:;xx## 	s   6A AAc                    | dz  }	 t        j                  |j                  d            }|j                  di       }t        j                  t        j                        }|t        d      z
  }i }|j                         D ]R  \  }}|j                  dd      }	|	d	k(  r|||<   #|	d
k(  s)|j                  d      }
|
s=	 t        |
      }||k\  r|||<   T |S # t        $ r i cY S w xY w# t        $ r Y sw xY w)uT   task-timers.json 로드 → running 작업 + 24시간 이내 completed 작업 반환task-timers.jsonr   r   tasks   hoursstatusr   running	completedend_time)r-   r.   r/   r0   r1   r   nowr   utcr   items	_parse_dt)r(   r3   r4   r9   rA   cutoffresulttask_idtaskr=   end_time_strr@   s               r5   load_task_timersrJ   ^   s   ((Dzz$..'.:; $" 5 ,,x||
$C92&&FF (B'Y"F7O{"88J/L(6H6)*.w M+  	$ ! s#   7C  C*C'&C'*	C65C6c                 *   | dz  }g }|j                         s|S |j                         D ]W  }|j                  }|j                  d      s!	 t	        j
                  |j                  d            }|j                  |       Y |S # t        $ r Y gw xY w)u   events/*.done 파일 스캔 (.done.acked, .done.escalated 등 확장자 파일 자동 제외) → 미처리 완료 작업 목록r+   z.doner   r   	existsiterdirnameendswithr-   r.   r/   appendr1   )r(   
events_dirresultsfrO   r4   s         r5   scan_done_filesrU   |   s    H$J$&G! 	vv}}W%	::akk7k;<DNN4 	 N  		s   6B	BBrG   c                    |dz  }||  dz  }|j                         sy	 |j                  d      }i }dD ]v  }d| d}t        j                  ||t        j
                        }|s1|j                  d	      j                         }	|	j                  d
      d   j                         }
|
||<   x |r|S dS # t        $ r Y yw xY w)u8   reports/<task_id>.md 에서 SCQA 추출. 없으면 None.reportsz.mdNr   r   )SCQAz\*\*z%\*\*:\s*(.+?)(?=\n\*\*[SCQA]\*\*:|\Z)   
r   )	rM   r/   r1   researchDOTALLgroupstripsplit)rG   r(   reports_dirr3   contentscqakeypatternmatchvalue
first_lines              r5   extract_report_scqarl      s    Y&KG9C(D;;=..'.2 D# ##CD		'7BII6KKN((*ET*1-335J"DI# 4!T!  s   B5 5	C Cc                     | dz  dz  }	 t        j                  |j                  d            }|S # t        $ r i cY S w xY w)u   session-guidance.json 로드whisperzsession-guidance.jsonr   r   )r-   r.   r/   r1   r2   s      r5   load_guidancero      sI    i"99Dzz$..'.:; 	s   &1 ??cwdc                    |dz  }i }|j                         s|S d}| rt        D ]
  }|| v s|} n |r-||z  dz  }|j                         r	 t        |      }|||<   |S |S t	        |j                               D ]D  }|j                         s|dz  }|j                         s*	 t        |      }|||j                  <   F |S # t        $ r Y |S w xY w# t        $ r Y dw xY w)u   
    프로젝트 context.md 로드.

    cwd 인자에서 프로젝트명 추출 (insuwiki/threadauto/dev-system 포함 시).
    없으면 모든 프로젝트 context.md 첫 줄 요약.

    반환: {project_name: first_line}
    projectsNz
context.md)rM   r'   _read_first_liner1   sortedrN   is_dirrO   )	rp   r(   projects_dirrF   matched_projectkeywordcontext_pathrk   proj_dirs	            r5   load_project_contextr{      s$    j(LF  &*O
' 	G#~")	
 #o5D -l;
*4'  M6M |3356 		H??$#l2L""$!1,!?J,6F8==)		 M   M ! s$   C $C	CC	CCc                 :   | dz  dz  }g }|j                         s|S |j                         D ]\  }|j                  }d|v r|j                  d      s&	 t	        j
                  |j                  d            }|j                  |       ^ |S # t        $ r Y lw xY w)u1   events/questions/*.json 로드 (.answered 제외)r+   	questionsz	.answeredz.jsonr   r   rL   )r(   questions_dirrS   rT   rO   r4   s         r5   load_questionsr      s    x'+5M$&G!""$ vv$}}W%	::akk7k;<DNN4  N  		s   6B	BBc                 >   | dz  }	 |j                  d      }g }|j                         D ]  }d|vr|}t        j                  dd|      }t        j                  dd|j                               }t        j                  dd|j                               }t        j                  d	d
|      }t        j                  dd
|      }t        j                  dd
|      }|j                         }|s|j                  t        |d              |S # t        $ r g cY S w xY w)u  MEMORY.md에서 ★ 포함 라인만 추출하여 간결한 리마인더 문자열 목록 반환.

    마크다운 서식(##, **, ~~, 취소선 등)을 제거하고 핵심 설명만 추출.
    파일 없거나 에러 시 빈 리스트 반환 (best-effort).
    z	MEMORY.mdr   r   u   ★z^#+\s*r   z	^\d+\.\s*z^[-*]\s*z	~~(.+?)~~z\1z\*{1,2}(.+?)\*{1,2}z_{1,2}(.+?)_{1,2}(   )max_len)r/   r1   
splitlinesr^   subrb   rQ   	_truncate)r(   r3   re   	reminderslinecleaneds         r5   load_memory_remindersr      s    k!D..'.2 I""$ =&&B0&&r7==?;&&b'--/:&&ug6&&/@&&-ug>--/Yw;<#=& /  	s   D DDc                 Z    	 ddl m} | dz  }| dz  } |||      S # t        $ r g cY S w xY w)u   memory-check-log.json과 task-timers.json을 비교하여 MC 미발급 태스크를 반환.

    best-effort: 모듈 임포트 실패 시 빈 리스트 반환.
    r   )get_unchecked_taskszmemory-check-log.jsonr8   )log_pathtimers_path)utils.memory_checkr   r1   )r(   r   r   r   s       r5   _load_unchecked_tasksr   !  sA    
:55!33"H+NN 	s    **c                 (   | dz  }	 t        j                  |j                  d            }g }|j                  dg       D ]>  }|j                  d      }|j                  d      }|(|+|j	                  ||d       @ |S # t
        $ r g cY S w xY w)u   active-projects.json에서 진행 중인 프로젝트 이름과 진행률 반환.

    반환: [{"name": "Memory Automation System", "progress": 90}, ...]
    파일 없거나 에러 시 빈 리스트 반환.
    zactive-projects.jsonr   r   activerO   progress)rO   r   )r-   r.   r/   r0   rQ   r1   )r(   r3   r4   rF   projrO   r   s          r5   load_project_progressr   0  s     ,,D
zz$..'.:;')HHXr* 	DD88F#Dxx
+HH$8tBC		D
  	s   A B (B +B BBc                 \   | dz  }	 t        j                  |j                  d            }|j                  di       }t        j                  t        j                        }t        t              }g }|j                         D ]  \  }}|j                  d      dk7  r|j                  dd	      }	|	s0	 t        |	      }
||
z
  }||k\  rAt        |j                         d
z        }|j                  ||j                  dd	      |d        |S # t        $ r g cY S w xY w# t        $ r Y w xY w)u   task-timers.json에서 status==pending이고 STALE_DAYS_THRESHOLD일 이상 경과한 태스크 반환.

    반환: [{"task_id": "...", "description": "...", "days_since": 5}, ...]
    파일 없거나 에러 시 빈 리스트 반환.
    r8   r   r   r9   )daysr=   pending
start_timer   iQ description)rG   r   
days_since)r-   r.   r/   r0   r1   r   rA   r   rB   r   STALE_DAYS_THRESHOLDrC   rD   inttotal_secondsrQ   )r(   r3   r4   r9   rA   	thresholdrF   rG   rH   	start_strstart_dtelapsedr   s                r5   load_stale_tasksr   D  s2    ((Dzz$..'.:; $" 5 ,,x||
$C34I#%F 88H*HH\2.		 +HHnG)# !6!6!85!@A
#*'+xxr'B&0* M9  	2  		s$   7D 4ADDD	D+*D+feedback_dirc                 l   	 | j                         sg S t        | j                  d            }|sg S t        |d d      dd }g }t	        j
                  dt        j                        }|D ]  }	 |j                  d	      }|j                         j                  }t        j                  |      j                  d
      }|j                  |      }	|	r|	j                  d      j                         n|j                   }
|j#                  |
|d        |S # t$        $ r Y w xY w# t$        $ r g cY S w xY w)u   feedback_dir에서 feedback_*.md 파일 스캔, 최신 3개의 name/date 반환.

    반환: [{"name": "직접 코딩 금지 위반", "date": "2026-04-09"}, ...]
    디렉토리 없거나 에러 시 빈 리스트 반환.
    zfeedback_*.mdc                 6    | j                         j                  S N)statst_mtime)rT   s    r5   <lambda>z&load_recent_mistakes.<locals>.<lambda>z  s    16683D3D     T)rg   reverseNr   z^---\n.*?name:\s*(.+?)\n.*?---r   r   z%Y-%m-%dr\   )rO   date)rM   listglobrt   r^   compiler`   r/   r   r   r   fromtimestampstrftimer_   ra   rb   stemrQ   r1   )r   filesfiles_sortedrF   rh   rT   re   mtimedate_strri   rO   s              r5   load_recent_mistakesr   m  s$   ""$I\&&78Ie)DdSTVUVW')**>		J 		A++w+7))#11%8AA*Mw/16u{{1~++-AFFtX>?		     	s@   D% D% =D% 0B"DD% 	D"D% !D""D% %D32D3c                 L   | dz  }	 |j                         syd}t        j                  dt        j                        }|j	                  d      D ]4  }	 |j                  d      }|j                  |      }|t        |      z  }6 |S # t        $ r Y Dw xY w# t        $ r Y yw xY w)u   base_dir/learnings/*.md 에서 status==pending 발생 횟수 반환.

    반환: int (pending 레코드 총 카운트)
    디렉토리 없거나 에러 시 0 반환.
    	learningsr   zX^\*\*status\*\*:\s*pending\s*$|^-\s*\*\*status\*\*:\s*pending\s*$|^status:\s*pending\s*$z*.mdr   r   )	rM   r^   r   	MULTILINEr   r/   findalllenr1   )r(   learnings_dircountrh   rT   re   matchess          r5   load_pending_learningsr     s     {*M##%**gikiuiu
 ##F+ 	A++w+7!//'2W%		     s:   B :B 1BB 	BB BB 	B#"B#r,   c                    t        j                  t        j                        }t	        t
              }g }| j                         D ]  \  }}|dk(  r|j                  d      dk7  r!|j                  dd      }	 t        |      }||z
  }||k\  r>|j                         dz  }	t        j                  ||      }
|j                  ||
|	d        |S # t        $ r Y w xY w)	u   
    bot-activity.json bots 데이터에서 3시간 이상 유휴 팀 탐지.

    반환: [{"team_id": "dev2", "team_name": "2팀", "hours": 4.2}, ...]
    r;   r   r=   idlesincer     )team_id	team_namer<   )r   rA   r   rB   r   IDLE_THRESHOLD_HOURSrC   r0   rD   r   r   rQ   r1   )r,   rA   r   
idle_teamsbot_idinfo	since_strr   r   r<   r   s              r5   detect_idle_teamsr     s     ,,x||
$C 45I')J

 U?88H'HHWb)		i(EEkG)#--/$6)--ff=	!!#)%.!&,   		s   :AC	CCtask_timersc                 J   t        j                  t        j                        }t	        t
              }g }| j                         D ]  \  }}|j                  d      dk7  r|j                  dd      }|s0	 t        |      }||z
  }||k\  rt        |j                         dz  d      }	|j                  dd	      }
|
j                  d
d      }t        j                  ||      }|j                  |||	t        |j                  dd            d        |S # t        $ r Y w xY w)uC   running 상태가 GHOST_THRESHOLD_HOURS 이상인 태스크 탐지.r;   r=   r>   r   r   r   r\   r   unknownr   r   )rG   r   r<   desc)r   rA   r   rB   r   GHOST_THRESHOLD_HOURSrC   r0   rD   roundr   replacer   rQ   _short_descr1   )r   rA   r   ghostsrG   rH   r   startr   r<   r   bot_keyr   s                r5   detect_ghost_tasksr     s   
,,x||
$C 56I#%F$**, 88H*HH\2.		i(EEkG)#g335<a@((9i8!//'26)--gw?	#*%.!& +DHH]B,G H	2 M  		s   7BD	D"!D"c                    t        |      }t        |      }t        |      }t        |      }t	        | |      }t        |      }t        |      }	dg}
g }t        D cg c]	  }||v s| }}|D cg c]  }||vs|dk7  s| }}||z   }i }|j                         D ]\  }|j                  d      dk(  s|j                  dd      }|j                  d	d      }|j                  |g       j                  |       ^ t        |t              }t        t              }|D ]  }||   }t         j                  ||      }|j                  dd
      }|j                  |g       }||v }|r4t#        j$                  t&        j(                        }t+        t,              }g }d}|dd D ]  } t/        | j                  dd            }!d}"|s| j                  d      r	d| d    d}"| j                  dd      }#d}$|#r	 t1        |#      }%||%z
  |k\  rd}$|$r|j                  | d    |" d|! d       |j                  | d    |" d|!        d} |r(|j                  | ddj5                  |       d       V|j                  | ddj5                  |       d       ~||v r)|r'||   }&|j                  | d|&d    d|&d    d       |dk(  s|dk(  rP|sN|j                  d d      }'t7        |'      }(|(|j                  | d!|( d"       |j                  | d#       |j                  | d$        |s|
j                  d%       n#|
j                  d&d'j5                  |      z          g })|D ]  }*|*j                  dd      }+|+st9        |+|      },|,r|,j                  d(d      }-|,j                  d)d      }.|,j                  d*d      }/|,j                  d+d      }0d,t;        |-       d-t;        |.       d.t;        |/       d/t;        |0       }1|)j                  |+ d0|1        |)j                  |+        |)r$|
j                  d1dj5                  |)      z          n|
j                  d2       |rrg }2|j=                         D ]:  \  }3}4|4j?                  d3      jA                         }5|2j                  |3 d4|5        < |
j                  d5d'j5                  |2      z          |j                  d6d      }6|6r|
j                  d7|6        n|
j                  d8       |j                  d9g       }7|7r/d:j5                  d; |7dd< D              }8|
j                  d=|8        tC        |      }9|9r>|9D :cg c]  }:|:d>    d|:d?    d@ };}:|
j                  dAd'j5                  |;      z          tE        |      }<|<rZ|<D -cg c],  }-|-d    dt/        |-j                  dd             dB|-dC    dD. }=}-|
j                  dEdj5                  |=      z          tG        |F      }>|>r>|>D ?cg c]  }?dG|?d>    dH|?dI    d }@}?|
j                  dJ       |
jI                  |@       tK        |      }A|
j                  dK|A dL       |rg }B|D ]j  }/|/j                  dMdN      }C|/j                  dOd      }D|/j                  dd      }E|ErBj                  C dE dPD        UBj                  C d4D        l |
j                  dQdj5                  B      z          n|
j                  dR       |	r^g }F|	D ]2  }G|Gd   |v rtM        GdS         }HFj                  |GdT    d|H dU       4 Fr#|
j                  dVdj5                  F      z          	 dWdXl'm(}I  |I       }J	 JjS                  dYdZd<d[\      }K|KrWKD Hcg c])  }H|Hj                  d]dN       d4|Hj                  d^d       + }L}H|
j                  d_dj5                  |L      z          JjU                          	 |
j                  d`       tW        |      }M|MrWg }NMD ],  }ONj                  da|Od    d|OdT    db|OdS    dc|Od    dd	       . |
jY                  dedfdj5                  N      z          t[        |      }P|Pr6PD Qcg c]  }QdG|Q 	 }R}Q|
jY                  dedgdj5                  |R      z          t]        |      }S|Srg }TSD ]V  }U|Uj                  dd      }|j                  d	d      }t         j                  ||      }Tj                  |Ud    d| d       X |
jY                  dedhdj5                  T      z          g }VdW}WdW}X|D ]  }||   }|j                  dd
      }|j                  |g       }|r.Wdiz  }WVj                  t         j                  ||       dj       \||v r=||v r9Wdiz  }W||   }&Vj                  t         j                  ||       d|&d    d       |dk(  s|dk(  r0|s.Xdiz  }XVj                  t         j                  ||       d#       Vj                  t         j                  ||       d$        |j                  dk      }Yt#        j$                  t&        j(                        j_                         dld'j5                  V      WXta        |      ta        |      |Yddm	}Zdnj5                  |
      |ZfS c c}w c c}w # t2        $ r Y w xY wc c}:w c c}-w c c}?w c c}Hw # JjU                          w xY w# t2        $ r Y !w xY wc c}Qw )ouL   모든 소스를 읽어 XML 브리핑 문자열 + 상태 딕셔너리 반환)r(   )rp   r(   z<whisper-briefing>r   r=   r>   r   r   r   r   r;   TN   r   bot()r   FrG    u    ⚠️고스트?:z / u    (실질유휴)u
    작업중u   :봇점유(teamr   
processingr   u   :유휴(zh)u   :유휴u   :상태불명u   [팀] 정보없음u   [팀] z | rX   rY   rZ   r[   zS:z C:z Q:z A:u    — u	   [완료] u   [완료] 없음z# z: u   [프로젝트] guidanceu   [가이던스] u*   [가이던스] 이전 세션 정보 없음pending_dispatchesz, c              3   2   K   | ]  }t        |        y wr   )str).0ps     r5   	<genexpr>z#compile_briefing.<locals>.<genexpr>{  s     <1A<s   r   u   [대기디스패치] rO   r   %u   [진행률] u    (마지막 수정 r   u   일 전)u   [미착수] )r   z- z (r   u   [최근실수]u#   [미처리학습] 학습 피드백 u   건 미확인 (v1)from?questionz): u	   [질문] u   [질문] 없음r<   r   u   시간째 유휴u   [유휴경고] r   )MemoryIndexeru   작업 수행 피드백diarysummary)type_filterlimitlayertitlesnippetu   [최근메모리] z</whisper-briefing>u   ⚠️ z) u/   h째 running — 고스트? `task-timer.py end u   `로 정리 필요u   [고스트경고] u   [메모리 리마인더] u   [메모리미확인] r\   u
   :작업중saved_atok	last_runr=   briefing_summaryteams_active
teams_idledone_pendingquestions_pendingguidance_last_savederrorr]   )1r6   rJ   rU   ro   r{   r   r   r   valuesr0   r   
setdefaultrQ   _build_bot_occupationr   setr   r   rA   r   rB   r   r   r   rD   r1   join_idle_hoursrl   r   rC   lstriprb   r   r   r   extendr   r   utils.memory_indexerr   r_   closer   insertr   r   	isoformatr   )[rp   r(   r   r,   r   
done_filesr   project_ctxr}   r   lines
team_partsbordered_bot_idsother_bot_idsall_bot_idsrunning_by_teamrH   r   r   bot_occupation_dev_setr   r   r   r=   running_tasksis_dev_teamrA   ghost_threshold	task_strshas_only_ghoststr   
bot_suffixr   is_ghostr   occr   idle_hcompleted_partsdonerG   rf   scqa	parts_str
proj_parts	proj_namerk   cleanguidance_textr   pending_strprogress_datar   
prog_partsstale_tasksstale_partsmistakesmmistake_partspending_learningsq_partsfrmr   task_ref
idle_partsith_WMI
_w_indexer_diary_hits
_mem_partsghost_tasksghost_partsgtmemory_remindersrreminder_partsunchecked_tasksunchecked_partsutteam_parts_statusr   r   r   status_dicts[                                                                                              r5   compile_briefingrK    s   
 h/D"H5K (3Jh/H&3BK1I"4(J,-E
 J #1>QAIq>O> $P1(@Q%ZQPMP!M1K 8:O""$ A88H*hhy"-Googr2G&&w3::4@A +;DN>"H 0;F|!%%ff5	(I.'++FB7(,,x||,C'.CDOI"O"2A& ,"155#;<
"quuU|#$QuXJaJEE,3	  )) 4%KO;'+H $$)~j\4&HY%Z[$$)~j\4&%IJ&+O%,& !!YKqI1F0G"WX!!YKqI1F0Gz"RS~%+ (C;s6{m1S^DTTUVWv&L"8"-I +F!!!YKxxr"BC!!YKw"78=9:a0;d )*X

: 667
 "$O ,((9b)"7X>b!Ab!Ab!Ab!AYq\N#il^3y|nCPYZ[P\~^I""gYeI;#?@""7+, [5::o#>>?&'
 
%0%6%6%8 	7!Iz%%d+113E2eW56	7 	&J)??@
 LLR0M}o67AB ll/4Gii<<<,[M:;
 *8<M?LM!61Qz]O15M
M^ejj&<<=
 #H5K !
 |nAk!%%r*BCDDWXYZfXgWhhpq
 
 	^ejj&==>
 $>HAIJA2ai[1V9+Q7JJ%&]#
 /A	LL67H6II[\]
  	5A%%$CuuZ,HuuY+H#azXJ?@#b
34	5 	[5::g#667&'
 
 	HB)}.BwK AK 11#5EFG	H LL*UZZ
-CCD
>V
	$++,ESZbckt+uKXcdSTw!4 5Ri8L7MNd
d1EJJz4JJK 
LL&'
 %[1K 	B"Y-"[/):"R[MIxy{  }F  zG  yH  HZ  [	 	R-

;0GGH
 -h?,<=qBqc(==R4uzz.7QQR
 ,X>O! 	DBffY+Googr2G%))'7;I""bm_Ai[#BC		D
 	R05::o3NNO $&LJ ZF|(I.'++FB7AL$$(9(9&&(I'J*%UV~%&H*<AL (C$$(9(9&&(I'J+VYZ`VaUbbc%dev&L"8!OJ$$(9(9&&(I'J'%RS$$(9(9&&(I'J-%XY!Z$ #,,z2 LL.88:!JJ'89$ J ^2
#K 99U[((g ?PN % l N
 Kf e  , >s   "	k6,k66	k; k;k;<l ?l1l3ll9 %l$  .l.%l$ l9 +m	 	lll$ $l66l9 9	mm
bot_to_devc                    	 ddl m}  ||       j                         S # t        $ r Y nw xY wt	        |j                               }i }| j                         D ]  \  }}|j                  d      dk7  r|j                  dd      }|j                  dd      }|sB|j                  d	d      }	|	|v rY|j                  |      }
|
sm||j                  d
|      |d||
<    |S )u   논리적 팀이 물리 봇을 점유하는 경우를 탐지.

    BotStatusManager 사용 가능 시 위임, 실패 시 기존 로직 fallback.

    Returns: {dev_short_id: {"team": team_id, "task_id": task_id, "bot_id": bot_id}}
    r   )BotStatusManager)r   r=   r>   r   r   r   r   rG   )r   rG   r   )	utils.bot_statusrN  get_bot_occupationr1   r  r  rC   r0   r   )r   rL  rN  dev_short_ids
occupationrG   rH   r   r   
team_short	dev_owners              r5   r  r     s    5K8KKMM  
))+,M,.J$**, 88H*((9b)%$ __Wb1
& NN6*	88Iw7 %Jy!!, s    	++r$  c                     | j                         } | j                  d      r| dd dz   } t        j                  |       }|j                   |j                  t        j                        }|S )u4   ISO8601 문자열 파싱 → timezone-aware datetimeZNr   z+00:00)tzinfo)rb   rP   r   fromisoformatrW  r   r   rB   )r$  dts     r5   rD   rD   K  s[    		Azz#crFX				"B	yyZZx||Z,Ir   r   c                     	 t        |       }t        j                  t        j                        |z
  }t        |j                         dz        S # t        $ r Y yw xY w)uM   since 문자열로부터 유휴 시간(정수 시간) 계산. 실패시 None.r   N)rD   r   rA   r   rB   r   r   r1   )r   r   r   s      r5   r  r  W  sS    )$,,x||,u47((*T122 s   AA 	AAr3   c                 z    | j                  d      j                         D ]  }|j                         }|s|c S  y)u3   파일의 첫 번째 비어있지 않은 줄 반환r   r   r   )r/   r   rb   )r3   r   strippeds      r5   rs   rs   a  s=    0;;= ::<O r   r   r   c                 2    t        |       |k  r| S | d| dz   S )u&   설명을 max_len 자 이내로 축약N   …)r   )r   r   s     r5   r   r   j  s$    
4yG>E!!r   textc                 R    | j                         } t        |       |k  r| S | d| dz   S )u)   텍스트를 max_len 자 이내로 축약Nr^  )rb   r   )r_  r   s     r5   r   r   q  s/    ::<D
4yG>E!!r   rJ  c                     |dz  dz  }	 |j                   j                  dd       |j                  t        j                  | dd      d	       y
# t
        $ r Y y
w xY w)u    status.json 저장 (best-effort)rn   zstatus.jsonT)parentsexist_okFr   )ensure_asciiindentr   r   N)parentmkdir
write_textr-   dumpsr1   )rJ  r(   status_paths      r5   save_statusrk  y  sc    Y&6K   =tzz+ERST_fg s   AA 	AAc                     t        t        j                        dkD  rt        j                  d   nd } 	 t        |       \  }}t	        |       t        |       t        j                  d       y # t        $ rd}t        j                  t        j                        j                         ddddddd t        |      d	}t        |       t	        d       Y d }~~d }~ww xY w)Nr\   )rp   r   r   r   r   uO   <whisper-briefing>[에러] 위스퍼 브리핑 생성 실패</whisper-briefing>)r   sysargvrK  printrk  r1   r   rA   r   rB   r  r   exit)rp   outputrJ  eerror_statuss        r5   mainrt    s    (+CHH(9!tCa.37fK   HHQK  a !X\\2<<> "!"#'V

 	L!_``as   %A. .	C7ACC__main__)   )   )r)   N)Q__doc__r-   osr^   rm  r   r   r   pathlibr   typingr   r   environr0   r   __file__resolverf  _WORKSPACE_ROOTBASE_DIR_CONSTANTS_PATHopen_fload
_CONSTANTSFileNotFoundErrorJSONDecodeErrorr   r   _sys_workspace_rootr3   r  utils.org_loaderr   _build_bot_to_key_mapr   _build_short_labelsr   _get_dev_short_ids_short_labelsrC   r   r   dict__annotations__r   r   r   ImportErrorr'   ANU_FEEDBACK_DIRr   r6   rJ   rU   rl   ro   r{   r   r   r   r   r   r   r   r   r   r   tuplerK  r  rD   r  rs   r   r   rk  rt  __name__)tidlabels   00r5   <module>r     s  
  	 	 
 2 2    **..!13tH~7M7M7O7V7V7]7]3^_ 8+'(25EE	o	0 #BTYYr]
#
 "~~lB7;;L!L "|R8<<]AN !%Odii'		O,NJH ()MWdWjWjWl$meS[["%=u%D$mM4S>m#M% 2 4NDI4"7"9Kc3h9( G $s) Fab   (0  DcN  '/ t 4S> < &. d $tCH~2F , 8@ " " "HTRUWZRZ^D\ "6 $, D S#X  /3X ,hsm ,d ,RVWZ\_W_R` ,^ %- T d38n1E 0 ,4  D  S	  F ,4 D T#s(^8L  ,4 D T#s(^8L ( '/ &t &4S#X3G &R /? t 4SRUXCW > -5 T  4 DcN  tDcN/C  FDcN tDcN7K P O_h)	#h))-h)HLh)
3S#Xh)`	(tCH~ (4S> (VZ[^`dehjmem`n[nVo (V	 	 	3 8C= 4 C "c "C " ""C "# "s " ?G T#s(^ t 4 0 zF E# #4//0 J$ %n  	M VN	KsO   
M M-M :AM; M533M; MM M21M25M; ;NN