
    iB             )       D   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ZddlmZm	Z	 ddl
mZ ddlmZmZmZ 	 ej                   j#                  d e ee      j(                               ddlmZ  e        	 ddlmZ 	 ddlmZ  ej8                         ZdZ	 ddlm Z  ddlm!Z" ddl#m$Z$ ddl&m'Z'm(Z(  e$e)      Z*	 ddl+m,Z- dZ.	 ddl/m0Z1 dZ2	 ddl3m4Z5 dZ6	 ddl7m8Z9 dZ:	 ddl;m<Z= dZ>	 ddl?m@ZA dZB	 ddlCmDZE dZF	 ddlGmHZI dZJ	 ddlKmLZM  eMj                  d e ee      j(                  dz  d z              ZO eMj                  eO      ZQeOj                  j                  eQ       eQj                  ZUdZVererej                  d!      nd"ZX eej                  j                  d#eX            Z[e[d$z  d%z  Z\e[d$z  d&z  Z]ererej                  d'      nd(Z_ej                  j                  d)e_      Z`d*Za ej                         d+z  d,z  d-z  d$z  Zcej                  j                  d.      ej                  j                  d/      ej                  j                  d0      ej                  j                  d1      ej                  j                  d2      ej                  j                  d3      ej                  j                  d4      ej                  j                  d5      ej                  j                  d6      d7	Zd	 dd8lemfZfmgZgmhZh  eg       Zi ef       Zj eh       ZkdMdNdOdPdQdOdRdSdOdTdUdOdVZph dWZqekj                         D ci c]  \  }}||
 c}}ZrddXedz  deneeneef   f   fdYZsddZedz  defd[Ztd\eneeneef   f   deueneef      fd]Zv	 dd^edZedz  defd_Zwe`fd'edeneef   fd`Zxe`fdaedbed'ededz  fdcZyddaedbeddezddfdeZ{dfedenfdgZ|ddhZ}diedjeddfdkZ~dledeufdmZdneudoedeufdpZdneudqedeufdrZdseuddfdtZdleduezfdvZh dwZdledoedefdxZdledoedefdyZdneudenfdzZd{edenfd|Zh d}Zg d~ZdledneudefdZdledezfdZddiedledededee   f
dZdiedezddfdZdenfdZdedefdZddezdefdZdedezfdZdefdZdieddfdZddedfedlededenf
dZ	 	 	 	 ddfedlediededee   dee   dedefdZ!dedfedieddfdZdieddfdZdleddfdZdlediededdfdZdledee   fdZdfedee   fdZddneudezdeufdZdledee   fdZdlededdfdZdfedleddfdZddiedledededdf
dZdenfdZdledee   fdZddfedlededee   fdZdedee   fdZ	 	 	 	 ddee   dledediee   dedbee   denfdZdedeuen   fdZdededeuen   fdZdedfedenfdZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddfee   dlededeee      dee   dee   dee   dedediee   deez   dedee   dedbee   ded{ee   dededenf(dZddZe)dk(  r e        yy# e$ r Y w xY w# e$ r ded	ed
eddfdZY w xY w# e$ r dZdZY w xY w# e%$ r ddlm Z  ddlm!Z" ddl#m$Z$ Y w xY w# e%$ r dedefdZ-dZ.Y w xY w# e%$ r dZ1dZ2Y w xY w# e%$ r dZ5dZ6Y w xY w# e%$ r dZ9dZ:Y w xY w# e%$ r dZ=dZ>Y w xY w# e%$ r dZAdZBY w xY w# e$ r dZEdZFY w xY w# e%$ r dZIdZJY w xY w# e$ r dZUdZVY w xY w# e%$ r erceraej                  d9      Zlej                  d:      Zm enel      Zi enem      Zkemj                         D  ci c]  \  } }|el|     nc c}} w c}} Zjn!d;d<d=d>d?d@dAdBdCZid;d<d=d>d?d@dAdBdDZjdEdFdGdHdIdJdKdLdCZkY |w xY wc c}}w )u#  
작업 위임 디스패처 (dispatch.py)

아누가 팀장에게 작업을 위임할 때 사용.
cokacdir --cron으로 독립 세션을 생성하여 현재 대화를 막지 않음.

Usage:
    # 권장: 지시 내용을 파일에 먼저 작성 후 --task-file로 전달
    python3 dispatch.py --team dev1-team --task-file /path/to/task-desc.md [--level normal|critical|security]
    # 짧은 한 줄 작업만 --task 직접 전달 (100자 이내 권장)
    python3 dispatch.py --team dev2-team --task "간단한 버그 수정" --level critical
    N)datetime	timedelta)Path)AnyListOptionalload_env_keys)atomic_json_writepathdata_kwreturnc                 F   ddl }t        |       }|j                  j                  dd       |j	                  t        |j                        d      \  }}	 t        j                  |dd	      5 }t        j                  ||d
d       |j                          t        j                  |j                                ddd       t        j                  |t        |             y# 1 sw Y   )xY w# t        $ r' 	 t        j                  |        # t         $ r Y  w xY ww xY w)u@   fallback: utils.atomic_write 로드 실패 시 인라인 구현.r   NTparentsexist_okz.tmp)dirsuffixwutf-8encoding   F)indentensure_ascii)tempfiler   parentmkdirmkstempstrosfdopenjsondumpflushfsyncfilenoreplaceBaseExceptionunlinkOSError)r   r   r   _tftargetfdtmp_pathfs           /home/jay/workspace/dispatch.pyr   r   (   s    dD48{{s6=='9&{IH	2sW5 %		$!%@	$% JJxV-	% %
  			(#   	sI   C0 /AC$<'C0 $C-)C0 0	D :DD 	DD DD )ConfigManagerTF)	TEAM_INFO)build_prompt)
get_logger)COMPOSITE_ALLOWED_TEAMSMAX_COMPOSITE_TEAMS)redact_sensitive_texttextc                     | S N )r:   s    r2   _redact_textr>   _   s        )scan_content)check_command)log_file_operation)route_model)should_sanitize)BotStatusManager)SessionResilienceimage_skill_routertoolszimage-skill-router.pyzroots.workspacez/home/jay/workspaceWORKSPACE_ROOTmemoryzorganization-structure.jsonztask-timer.pychat_id
6937032012COKACDIR_CHAT_IDi   z.claudeprojectsz%-home-jay--cokacdir-workspace-autosetCOKACDIR_KEY_ANUCOKACDIR_KEY_DEV1COKACDIR_KEY_DEV2COKACDIR_KEY_DEV3COKACDIR_KEY_DEV4COKACDIR_KEY_DEV5COKACDIR_KEY_DEV6COKACDIR_KEY_DEV7COKACDIR_KEY_DEV8)	anudev1dev2dev3dev4dev5dev6dev7dev8)build_bot_to_key_mapbuild_team_bot_mapbuild_team_to_bot_id_mapteamsteam_to_botrY   rZ   r[   r\   r]   r^   r_   r`   )	dev1-team	dev2-team	dev3-team	dev4-team	dev5-team	dev6-team	dev7-team	dev8-teambot-bbot-cbot-dbot-ebot-fbot-gbot-hbot-iro   rp   rq   rr   rs   rt   ru   rv   u   마아트 (Ma'at)u   QC 매니저)nameroleu   로키 (Loki)u   레드팀 리더u   비너스 (Venus)u   디자인 디렉터u   야누스 (Janus)DevOps)qcredteamdesigndevops>   r|   content	marketing
consulting
publishingexclude_task_idc                    t         r&t         t        t              j                  |       S t        dz  dz  }i }|j	                         s|S 	 t        |dd      5 }t        j                  |      }ddd       j                  d	i       j                         D ]i  \  }}|j                  d
      dk7  r| r|| k(  r#|j                  dd      }|t        v rt        |   }||d||<   |j                  d      }	|	sb||d||	<   k 	 |S # 1 sw Y   xY w# t        j                  t        f$ r#}
t        j                  d|
        Y d}
~
|S d}
~
ww xY w)u   task-timers.json에서 running 상태 봇의 점유 정보 반환.

    Args:
        exclude_task_id: 이 task_id의 엔트리는 결과에서 제외 (자기 자신 제외용)

    Returns:
        {bot_id: {"task_id": ..., "team_id": ...}} 매핑
    Nworkspace_rootr   rJ   task-timers.jsonrr   r   tasksstatusrunningteam_id )task_idr   botu4   task-timers.json 읽기 실패 (봇 점유 정보): )_BOT_STATUS_AVAILABLE_BotStatusManager	WORKSPACEget_busy_botsexistsopenr$   loadgetitemsTEAM_TO_BOT_IDJSONDecodeErrorr,   loggerwarning)r   
timer_filebusyr1   r   r   
task_entryr   r   	bot_fieldes              r2   _get_busy_bots_infor     s`    !2!> 	:HHYhHii X%(::J&(DS*cG4 	 99Q<D	 #'88GR#8#>#>#@ 	KGZ~~h'947o#= nnY3G.($W-(/GDS	"u-I.5'"JY	K" K'	  	     '* SMaSQRRKSs7   D D	2B	D <
D 	DD E.EErequired_modelc           
         t        t               j                               }i }| rct               }dD ]T  }t        j                  |d      }t        j                  |d      }t        j                  |d      }|sH||v sM||   ||<   V dD ]P  }||v r| rD|j                  |d      | k7  r/t        j                  d| d|j                  |d       d|         N|c S  | rt        d|  d      t        d	      )
u  task-timers.json에서 running 상태 확인 후 가용 봇 반환.

    우선순위: bot-b > bot-c > bot-d > bot-e > bot-f > bot-g > bot-h > bot-i
    dev team running → 해당 봇 사용 중
    composite/marketing/design running → bot 필드로 판별

    Args:
        required_model: 필요한 모델명 (예: "claude-opus-4-6"). 지정 시 해당 모델의 봇만 선택.

    Returns:
        "bot-b" | "bot-c" | ... | "bot-i"
    Raises:
        RuntimeError: 조건에 맞는 가용 봇이 없을 때
    rn   r   [model-filter]     스킵: model=unknown    ≠    모델 조건(i   )에 맞는 가용 봇이 없습니다. 모든 opus 봇이 작업 중이거나 모델 미설정입니다.E   모든 봇이 작업 중입니다. 잠시 후 다시 시도하세요.)setr   keys_read_bot_modelsBOT_TO_DEFAULT_TEAMr   TEAM_BOTBOT_KEYSr   infoRuntimeError)	r   	busy_botsbot_model_map
key_modelsbot_iddefault_teamkey_namekey_hashr   s	            r2   _find_available_botr   1  s)    ')..01I %'M%'
^ 	=F.2262>L||L"5H||Hb1HH
2(28(<f%	= X )m//R8NJKK/#om>O>OPSU^>_=``efteuvw
 ^, -P Q
 	
 ^
__r?   r   c                 z    g d}g }|D ]/  }|| vs|j                  |t        j                  |d      d       1 |S )u  전체 봇 중 busy가 아닌 가용 봇 목록과 기본 팀 매핑을 반환.

    Args:
        busy_bots: _get_busy_bots_info()의 반환값 {bot_id: {"task_id": ..., "team_id": ...}}

    Returns:
        [{"bot_id": "bot-c", "default_team": "dev2-team"}, ...]
    rn   r   )r   r   )appendr   r   )r   all_bots	availabler   s       r2   _get_available_bots_with_teamsr   ]  sT     XHI i!$7$;$;C$K r?   timer_task_idc           
      &   t         dz  dz  }t         dz  dz  }|j                  j                  dd       d}	 t        |d      }t	        j
                  |t        j                         t               }|j                         r	 t        |dd	
      5 }t        j                  |      }ddd       j                  di       j                         D ]w  \  }}	|	j                  d      dk7  r|| k(  r!|	j                  dd      }
|
t        v r|j                  t        |
          |	j                  d      }|sg|j                  |       y 	 i }|rct'               }dD ]T  }t(        j                  |d      }t*        j                  |d      }t,        j                  |d      }|sH||v sM||   ||<   V d}dD ]P  }||v r|rD|j                  |d      |k7  r/t"        j/                  d| d|j                  |d       d|        N|} n ||rt1        d| d      t1        d      |j                         r	 t        |dd	
      5 }t        j                  |      }ddd       j                  di       j                  |       }|rQ||d<   t        |dd	
      5 }t        j2                  ||dd       ddd       t"        j/                  d|  d| d       ||6	 t	        j
                  |t        j4                         |j7                          S S # 1 sw Y   lxY w# t        j                  t         f$ r#}t"        j%                  d|        Y d}~d}~ww xY w# 1 sw Y   xY w# 1 sw Y   xY w# t        j                  t         f$ r%}t"        j%                  d |  d!|        Y d}~d}~ww xY w# t8        $ r Y S w xY w# |E	 t	        j
                  |t        j4                         |j7                          w # t8        $ r Y w w xY ww xY w)"u   봇 선택과 타이머 기록을 원자적으로 수행.

    파일 락을 잡은 상태에서:
    1. busy bots 확인
    2. 가용 봇 선택
    3. task-timers.json에 bot 필드 즉시 기록

    이렇게 해야 동시 dispatch 간 봇 충돌을 방지할 수 있음.

    Args:
        timer_task_id: 봇을 예약할 태스크 ID
        required_model: 필요한 모델명 (예: "claude-opus-4-6")

    Returns:
        선택된 봇 ID (예: "bot-b")
    Raises:
        RuntimeError: 조건에 맞는 가용 봇이 없을 때
    rJ   r   .task-timers.lockTr   Nr   r   r   r   r   r   r   r   r   r   u-   task-timers.json 읽기 실패 (봇 예약): rn   r   r   r   r   r   r   r   Fr   r   r   u   [봇 예약]     → u    (원자적 예약 완료)u   봇 예약 기록 실패 (): )r   r   r   r   fcntlflockLOCK_EXr   r   r$   r   r   r   r   addr   r,   r   r   r   r   r   r   r   r   r%   LOCK_UNclose	Exception)r   r   r   lock_file_pathlock_fdr   r1   r   tidentryteamr   r   r   r   r   r   r   r   selected_botr   r   s                         r2   _select_and_reserve_botr   s  s   , X%(::J),??Nt<GK~s+GU]]+ "e	T*cG< (99Q<D("&((7B"7"="="? 
1JCyy*i7 m+  99Y3D~-!nT&:; %		% 0I !i0
1 )+)+Jb A266vrB#<<b9#<<"5J 6,6x,@M&)A $([ 	Ci-"3"3C"<"NocU/-BSBSTWYbBcAddijxiyz{L	 "$^$4 5X Y  fgg 
S*cG< (99Q<D(!XXgr266}E
(4Ju%j#@ IA		$aHIKK-eL>Qk lm GU]]3 }( ( (('2 T!NqcRSSTH( (
I I (('2 S!;M?#aSQRRS  	 GU]]3 	 s   A
O L L(BL 7L 
AO #O (BO 8M7 M>M7 M+4$M7 O 4N8LL M8MO MO M(#M7 +M40M7 7N5N0+O 0N55O 8	OOP4P ?P 	P	PPPc                    t        j                         dz  dz  }|j                         si S 	 t        |dd      5 }t	        j
                  |      }ddd       i }j                         D ]:  \  }}|j                  di       }|j                  t        |       d      }|s6|||<   < |S # 1 sw Y   ZxY w# t        $ r$}	t        j                  d	|	        i cY d}	~	S d}	~	ww xY w)
u   bot_settings.json에서 봇 키 해시별 모델 설정을 읽어 반환.

    Returns:
        {key_hash: model_name} 예: {"c38fb9955616e24d": "claude-opus-4-6"}
    	.cokacdirbot_settings.jsonr   r   r   Nmodelsr   u.   [bot_models] bot_settings.json 읽기 실패: )r   homer   r   r$   r   r   r   r!   r   r   debug)
rK   settings_pathr1   settingsresultr   cfgr   modelr   s
             r2   r   r     s     IIK+-0CCM!	-w7 	$1yy|H	$%^^- 	)MHcWWXr*FJJs7|R0E#(x 		)
 	$ 	$  EaSIJ	s;   B7 B+AB7 "B7 +B40B7 7	C$ CC$C$r   r   c                    t        j                         dz  dz  }|j                         st        j	                  d       y	 t        |dd      5 }t        j                  |      }ddd       j                  |       }|st        j	                  d|         y|j                  d	i       }|j                  t        |            }||t        |      <   ||d	<   ||| <   t        |d
d      5 }t        j                  ||dd       ddd       t        j                  d|  d| d|        |S # 1 sw Y   xY w# 1 sw Y   5xY w# t        $ r"}	t        j                  d|	        Y d}	~	yd}	~	ww xY w)u"  bot_settings.json에서 특정 봇의 모델을 변경한다.

    Args:
        key_hash: 봇 키 해시 (예: "c38fb9955616e24d")
        model: 설정할 모델명 (예: "claude-opus-4-6")
        chat_id: 채팅 ID

    Returns:
        이전 모델명 또는 None (변경 실패)
    r   r   u)   [model-override] bot_settings.json 없음Nr   r   r   u(   [model-override] 봇 설정 없음: key=r   r   Fr   r   u(   [model-override] 봇 모델 변경: key=, r   u+   [model-override] 봇 모델 변경 실패: )r   r   r   r   r   r   r$   r   r   r!   r%   r   r   error)
r   r   rK   r   r1   r   bot_cfgr   
prev_modelr   s
             r2   _set_bot_modelr     sa    IIK+-0CCM!BC-w7 	$1yy|H	$,,x(NNEhZPQXr*ZZG-
$s7|"$-w7 	A1IIha@	A>xj:,V[\a[bcd	$ 	$	A 	A  B1#FGsO   E D.&3E AE ,D:'E .D73E :E?E 	E1E,,E1delayc                     d|  dt          d| d}t        j                  ddd| d| gd	t        j                  t        j                  
       t        j                  d| d|  d|        y)u   delay초 후 봇 모델을 복원하는 백그라운드 프로세스를 포크한다.

    세션이 시작된 후 모델을 원래대로 복원하여 다음 일반 작업에서 Sonnet이 사용되도록 한다.
    z}import json, pathlib; p=pathlib.Path.home()/'.cokacdir'/'bot_settings.json'; d=json.loads(p.read_text(encoding='utf-8')); d['z']['models']['z']='zK'; p.write_text(json.dumps(d,ensure_ascii=False,indent=2),encoding='utf-8')python3z-czimport time; time.sleep(z); T)start_new_sessionstdoutstderrz[model-override] u"   초 후 모델 복원 예약: key=r   N)CHAT_ID
subprocessPopenDEVNULLr   r   )r   r   r   restore_scripts       r2   _schedule_model_restorer     s    	 Z~gYd5' :S	T  	D4UG3~>NOP!!!!	 KK#E7*LXJV[\a[bcdr?   r   c           	         ddd| d}	 d}t         j                         rt        t         dd      5 }t        j                  |      }ddd       j                  di       j                  d	i       j                  d
g       }|D ]  }|j                  d      | k(  r&|j                  di       }|j                  dd      } nW|j                  dg       D ]<  }|j                  d      | k(  s|j                  di       }|j                  dd      } n |s n d}	t        j                  |       }
|
rt        j                  |
      }|rt        j                         dz  dz  }|j                         rmt        |dd      5 }t        j                  |      }ddd       j                  |i       }|j                  di       j                  t        t              d      }	||d<   |	|d<   |r-|	r+||	k7  r&d|d<   t        j                  d|  d| d|	 d       |S t        j                  d|  d|xs d d|	xs d d       	 |S # 1 sw Y   xY w# 1 sw Y   xY w# t        $ r#}t        j                  d |        Y d}~|S d}~ww xY w)!u  org-structure.json과 bot_settings.json의 모델 설정을 비교 검증한다.

    org-structure.json에서 해당 team_id의 lead.model 값과
    bot_settings.json에서 해당 봇의 models[chat_id] 값을 비교하여
    불일치 시 WARNING 로그를 출력한다.
    디스패치는 bot_settings.json 기준을 유지한다.

    Args:
        team_id: 검증할 팀 ID (예: "dev8-team")

    Returns:
        dict: {"consistent": bool, "org_model": str, "bot_model": str, "team_id": str}
    Tr   )
consistent	org_model	bot_modelr   r   r   r   N	structurecolumnsrd   r   leadr   	sub_teamssub_team_idr   r   r   r   r   Fr   u   [모델 불일치] z: org-structure=z, bot_settings=u/    — 디스패치는 bot_settings 기준 유지u   [모델 일관성] z: org=N/A, bot=u     — 일치 또는 확인 불가u(   [모델 검증] 검증 실패 (무시): )ORG_FILEr   r   r$   r   r   r   r   r   r   r!   r   r   r   r   r   )r   r   r   r1   org_datard   r   r   subr   r   r   r   r   r   r   s                   r2   _validate_model_consistencyr   -  s    #'RbU\]F1G	??hg6 (!99Q<(LLb155iDHHRTUE 88I&'188FB/D $" 5I88K4 Cww}-8"wwvr2$(HHWb$9		
   	<<(||H-H $		k 9<O O '')mS7C 0q#'99Q<0&ll8R8G 'Hb 9 = =c'lB OI'{'{yI'=#(F< NN%gY.>yk J  ){*Y[ M KK%gYfY5G%4H
S\Se`eRf  gG  H M_( (40 0$  GA!EFFMGs[   (I IB"I *)I A+I ?IB I 'I II II 	J#JJc                  n   ddl } 	 t        j                         dz  dz  }|j                         st        j                  d       yt        |dd      5 }t        j                  |      }ddd       | j                        }|j                         D ]  \  }}d	|v sd
|d	<    t        dz  dz  }|j                  j                  dd       t        |dd      5 }t        j                  ||dd       ddd       t        j                  d|        	 t        j                         dz  dz  }|j                         syt        |dd      5 }t        j                  |      }ddd       t        dz  dz  }	t        |	dd      5 }t        j                  |      }
ddd       
j%                  dd      }j                         D ]R  \  }}|j%                  dd      }|j%                  dd      }|j%                  di       }|j%                  di       }|r|sT|j'                  d      }t)        |      dk\  r0|d   j+                  d      r|d   dd j-                         r|d   }nd }|r
||v r||   }n'|r#t/        t1        |j3                                     }nd}|r
||v r||   }n'|r#t/        t1        |j3                                     }nd}||||d!|
j5                  d"i       |<   |d k7  s,| d#}|
j5                  d$i       j5                  ||       U t7        j8                         j;                  d%      |
j5                  d&i       d'<   t        |	dd      5 }t        j                  |
|dd       ddd       t        j                  d(       y# 1 sw Y   2xY w# 1 sw Y   xY w# t         $ r#}t        j#                  d|        Y d}~d}~ww xY w# 1 sw Y   xY w# 1 sw Y   TxY w# 1 sw Y   xY w# t         $ r"}t        j#                  d)|        Y d}~yd}~ww xY w)*uY  bot_settings.json의 민감 정보(token)를 마스킹한 사본을 workspace에 저장한다.

    원본의 "token" 키 값을 "***REDACTED***"로 교체한 사본을
    {WORKSPACE}/memory/bot_settings_sync.json에 저장한다.
    기타 필드는 그대로 유지된다.
    실패해도 디스패치 자체는 중단되지 않는다.
    r   Nr   r   u>   [bot_settings_sync] bot_settings.json 없음, 동기화 스킵r   r   r   tokenz***REDACTED***rJ   zbot_settings_sync.jsonTr   r   Fr   r   u&   [bot_settings_sync] 동기화 완료: u/   [bot_settings_sync] 동기화 실패 (무시): configconstants.jsonrK   r   display_nameusernamer   last_sessions_   devrX   )r  r  team_dirr   bots-teamrd   z%Y-%m-%dmetalast_updatedu;   [bot_settings_sync] constants.json 봇 정보 반영 완료uA   [bot_settings_sync] constants.json 업데이트 실패 (무시): )copyr   r   r   r   r   r   r$   r   deepcopyr   r   r   r   r%   r   r   r   r   splitlen
startswithisdigitnextitervalues
setdefaultr   nowstrftime)r  r   r1   r   maskedr   r   	sync_pathr   constants_path	constantschat_id_key	_key_hashr  r  r   r  partsbot_short_idr   r  r   s                         r2   _sync_bot_settingsr$  r  s    N		k14GG##%LLYZ-w7 	$1yy|H	$ x(#\\^ 	0MHc#~/G	0 (+CC	td;)S73 	?qIIfaeA>	? 	<YKHI
C`		k14GG##%-w7 	$1yy|H	$ #X-0@@.#8 	%A		!I	%  mmIr2&nn. +	TNIs # ;LGGJ3H778R0F"%''/2">Mx !&&s+E5zQ58#6#6u#=%(12,BVBVBX$Qx  % {f4#K0T&--/23{m; -k :]%9%9%; <= !-$$	>I  ,\: u$)N%0$$Wb1<<WlSW+	T\ <D<<>;R;RS];^	VR(8.#8 	BAIIiqA	B 	QRi	$ 	$	? 	?  NHLMMN	$ 	$	% 	%j	B 	B  `Z[\Z]^__`s   ?N4 N4 N*3N4 >N4 N'6 N4 *P	 P	 O#&"P	 O0EP	 4A.P	 "O=<P	 N$N4 'N1,N4 4	O =OO #O-(P	 0O:5P	 =PP	 		P4P//P4r   metadatac                 4   t         dz  dz  }t         dz  dz  }|j                  j                  dd       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i       j                  |       }|r8|j                  |       t        ||       t         j#                  d|  d|        |6	 t	        j
                  |t        j                         |j                          yy# t        $ r Y yw xY w# 1 sw Y   xY w# t        $ r%}t         j%                  d|  d|        Y d}~}d}~ww xY w# t        $ r Y yw xY w# |E	 t	        j
                  |t        j                         |j                          w # t        $ r Y w w xY ww xY w)uI   task-timers.json에서 지정된 task_id 항목에 메타데이터 추가rJ   r   r   Tr   Nr   r   r   r   r   u   메타데이터 패치 완료: r   u   메타데이터 패치 실패 (r   )r   r   r   r   r   r   r   r   r   r   r   r$   r   r   updater   r   r   r   )	r   r%  r   r   r   r1   r   r   r   s	            r2   _patch_timer_metadatar(    s   X%(::J),??Nt<G~s+GU]]+  " GU]]3  *cG4 	 99Q<D	 XXgr*..w7
h'j$/KK9'%zRS GU]]3   	  	   J8	QCHIIJ  	 GU]]3 	 s   A F =4E3 3F FA#F =4F? 3	E?>E?FF 	F<F72G 7F<<G ?	G
GH4HH	HHHH	task_descc           
         | j                         }|D ]  }|j                         }|j                  d      s%|t        d      d j                         }|sg c S |j	                  d      D cg c]#  }|j                         s|j                         % c}c S  d}g }|D ]  }|j                         }|r|j                  d      r |S |s,|j                  d      r#|j                  |dd j                                `|sg|j	                  d      D cg c]8  }|j                         st        j                  dd	|j                               : }}|D 	cg c]  }	|	s|		 c}	c S  |S |d
k(  sd} |S c c}w c c}w c c}	w )u  task_desc 문자열에서 affected_files 정보를 찾아 파일명 리스트를 반환한다.

    세 가지 형식을 지원한다:
    - 인라인: 'affected_files: server.py, app.js'
    - 섹션 목록: '## affected_files' 헤더 이후 '- path' 줄들
    - 섹션 인라인: '## affected_files' 헤더 이후 쉼표 구분 값 (괄호 코멘트 자동 제거)

    인라인 → 섹션 목록 → 섹션 인라인 순서로 시도한다.
    없으면 빈 리스트를 반환한다.
    zaffected_files:N,Fz##- r   z\s*\(.*?\)\s*$r   ## affected_filesT)
splitlinesstripr  r  r  r   rer   )
r)  lineslinestrippedvaluer1   
collectingr   r   items
             r2   _parse_affected_filesr7    s      "E  F::<01S!2356<<>E	',{{3'7E!1779AGGIEEF JF ::<""4( M ""4(hqrl0023KS>>Z]K^labcbibibk 12qwwyAll).7$77 M ,,J!$ M/ F  m7s$   -E9E9E>,'E>F!Faffected_filesr   c                    | s| S t        |      dz  dz  }|j                         st        j                  d|        | S 	 dt	        |      d|dg| d}t        j                  |ddd	
      }|j                  dk7  rBt        j                  d|j                   d|j                  j                         dd         | S t        j                  |j                        }t        |       }g }dD ]C  }|j                  |g       D ],  }	|	s|	|vs|j                  |	       |j!                  |	       . E |r't        j#                  dt%        |       d       | |z   S | S # t
        j&                  $ r t        j                  d       | cY S t        j(                  $ r%}
t        j                  d|
 d       | cY d}
~
S d}
~
wt*        $ r%}
t        j                  d|
 d       | cY d}
~
S d}
~
ww xY w)u  AST 의존성 스크립트를 호출하여 blast radius를 계산하고 affected_files를 보강한다.

    scripts/ast_dependency_map.py를 subprocess로 호출하여 직접 임포터(direct_importers)와
    테스트 파일(test_files)을 추출한 뒤 기존 목록에 중복 없이 병합한다.
    스크립트 실패·타임아웃·JSON 파싱 오류 시 원래 목록을 그대로 반환한다 (graceful fallback).
    scriptszast_dependency_map.pyu:   [ast-blast-radius] AST 스크립트를 찾을 수 없음: r   --root--filesz--jsonT   capture_outputr:   timeoutr   u5   [ast-blast-radius] 스크립트 비정상 종료 (rc=r   N   )direct_importers
test_filesu)   [ast-blast-radius] blast radius 보강: +u   개 파일 추가uE   [ast-blast-radius] 타임아웃(30s) — 원래 affected_files 사용u'   [ast-blast-radius] JSON 파싱 실패: u!    — 원래 affected_files 사용u"   [ast-blast-radius] 예외 발생: )r   r   r   r   r!   r   run
returncoder   r/  r$   loadsr   r   r   r   r   r   r  TimeoutExpiredr   r   )r8  r   
ast_scriptcmdr   r   existingextrakeyr1   r   s              r2   _enrich_affected_files_with_astrM  ,  s    n%	14KKJST^S_`a-
O
 
 
 	
 !NNGHYHYGZZ]bhbobobububwx|y|b}a~ "!zz&--(~&5 	$CXXc2& $(*LLOLLO$	$ KKCCJ<O`ab!E))$$ ^_ @Cdef ;A3>_`asQ   A=E ?AE E AE E *G-
G-F<6G-<G-G("G-(G-current_task_idc                 `   t         dz  dz  }|j                         sg S 	 t        |dd      5 }t        j                  |      }ddd       g }j                  di       }t        |       }|j                         D ]t  \  }	}
|	|k(  r|
j                  d	      d
k7  r!t        |
j                  dg             }||z  }|sDdj                  t        |            }|j                  d|	 d|        v |S # 1 sw Y   xY w# t
        $ r$}t        j                  d|        g cY d}~S d}~ww xY w)u  task-timers.json의 running 상태 task들과 affected_files 교집합을 확인한다.

    current_task_id는 제외(자기 자신).
    겹침 발견 시 경고 메시지 문자열 리스트를 반환한다.
    파일이 없으면 빈 리스트를 반환한다.
    rJ   r   r   r   r   Nu@   [_check_affected_files_overlap] task-timers.json 읽기 실패: r   r   r   r8  r   u   [파일 충돌 경고] u   (running)와 파일 겹침: )r   r   r   r$   r   r   r   r   r   r   r   joinsortedr   )r8  rN  r   r1   r   r   warningsr   affected_setr   	task_infoother_filesoverlapoverlap_lists                 r2   _check_affected_files_overlaprX  k  s9    X%(::J	*cG4 	 99Q<D	  HHHWb!E~&L#kkm 	ko%=="i/)--(8"=>,99VG_5LOO5gY>Z[gZhij	k O+	  	  YZ[Y\]^	s4   D  C4D  4C=9D   	D-	D("D-(D-rR  c                 (   | syt         j                  j                  dd      }|st        j	                  d       yddj                  |       z   }	 ddl}d| d	}t        j                  t        |d
      j                  d      }|j                  j                  ||ddi      }|j                  j                  |d       t        j                  dt        |        d       y# t         $ r"}t        j	                  d|        Y d}~yd}~ww xY w)u^   겹침 경고를 Telegram으로 발송한다. 실패해도 dispatch를 중단하지 않는다.NANU_BOT_TOKENr   uB   [overlap-telegram] ANU_BOT_TOKEN 미설정, Telegram 경고 스킵u%   ⚠️ affected_files 겹침 감지:

r   zhttps://api.telegram.org/botz/sendMessage)rK   r:   r   zContent-Typezapplication/json)r   headers
   )r@  u9   [overlap-telegram] 겹침 경고 Telegram 발송 완료 (u   건)u+   [overlap-telegram] Telegram 발송 실패: )r"   environr   r   r   rP  urllib.requestr$   dumpsr   encoderequestRequesturlopenr   r  r   )rR  	bot_tokenmessageurlliburlr   reqr   s           r2   _send_overlap_telegram_warningrj    s    

3I[\689LLG	J,YK|Dzzgw?@GGPnn$$StnN`=a$bsB/OPST\P]^bcd JDQCHIIJs   BC& &	D/DD
task_levelc                 6    |dk  ryt        |       }|sd| dS y)u   task_level >= 2이고 affected_files가 없으면 경고 문자열을 반환한다.

    그 외에는 None을 반환한다.
    r   Nu   [경고] Lv.um    작업에 affected_files가 기재되지 않았습니다. 영향받는 파일 목록을 추가해 주세요.)r7  )r)  rk  filess      r2   _warn_missing_affected_filesrn    s8    
 A~!),E:, 'A A	
 r?   >	   jqcatnpmnpxtsccurlgreppytestr   c                 X   d| v r| S t        j                  d|       }h d}|D cg c]5  }|j                  d      j                         |vs%|j                  d      7 }}t	        t
        j                  |            dd }t               }|D ]  }	 t        j                  ddd	d
ddd||g	ddd      }|j                  j                         j                  d      D ]4  }|s|j                  t        j                  j!                  ||             6  t)        |      dkD  r$t$        j'                  dt)        |       d       | S |r)ddj+                  d t-        |      D              z   }	| |	z  } | S c c}w # t        j"                  $ r t$        j'                  d|        Y w xY w)uW   task_desc에 ## affected_files 미기재 시, 백틱 코드 토큰 기반 자동 탐지r-  z`([A-Za-z_]\w*(?:\(\))?)`>   idrL  r   r6  listrw   r   typer   eventindexr   propsstater4  r  r   optionsz()Nr]  ru  z-rlz--include=*.pyz--include=*.tsz--include=*.tsxz--exclude-dir=node_modulesz--exclude-dir=.gitTr>  r[  u#   [auto-affected] grep 타임아웃:    z[auto-affected] u*   개 파일 감지 (>20) — 주입 안 함z$

## affected_files (auto-detected)
c              3   &   K   | ]	  }d |   yw)r,  Nr=   ).0r1   s     r2   	<genexpr>z._auto_inject_affected_files.<locals>.<genexpr>  s     GkUV"QCGks   )r0  findallrstriplowerry  dictfromkeysr   r   rD  r   r/  r  r   r"   r   relpathrG  r   r   r  rP  rQ  )
r)  r   tokensCOMMON_FILTERtaffectedr  r   r2  sections
             r2   _auto_inject_affected_filesr    s   i' ZZ4i@FGM '-\0D0D0Fm0[ahhtn\F\$--'("-F uH 	^^ 02BDU-/C(  $$	F ++-33D9 HLL~!FGH  8}r)#h-8bcd ;diiGkZ`aiZjGk>kkW	= ]  (( 	NN@HI	s$   &E5E5AE:#1E::+F)(F)c                     d| v r| S t        j                  d|       }|s| S g }|D ]/  }|j                         d   }|t        v s|j	                  |       1 |rd}|dd D ]  }|d| dz  } | |z  } | S )	uH   검증 시나리오에서 실행 가능한 goal_assertions 자동 생성z## goal_assertionsz:`((?:grep|curl|pytest|python3|tsc|cat|jq|npx|npm)\s[^`]+)`r   z&

## goal_assertions (auto-generated)
N   z- `z`
)r0  r  r  ALLOWED_COMMANDSr   )r)  r   commandssafe_commandsrI  
first_wordr  s          r2   _auto_generate_goal_assertionsr    s    y( zzWYbcH M &YY[^
))  %&
 = !$ 	&CSE~%G	&W	r?   c                    d}g }g }| D cg c]  }t        |      j                  d      s|! }}|sg g ddS |D ]  }t        |      }t        t        |      j                        }	t        |      j                  }
	 t        j                  d|d|	d|
gddd	
      }|j                  dk7  r6t        j                  d|
 d|j                   d|j                  dd         t        j                  |j                        }|j                  di       }|j                  |j                  dg              |j                  |j                  dg               t'        t(        j+                  |            }t'        t(        j+                  |            }||t-        |      t-        |      z   dS c c}w # t
        j                   $ r t        j                  d|
 d       Y t        j"                  $ r&}t        j                  d|
 d|        Y d}~d}~wt$        $ r&}t        j                  d|
 d|        Y d}~d}~ww xY w)u  affected_files의 .py 파일을 AST 분석하여 blast radius(영향 범위)를 반환한다.

    각 .py 파일에 대해 ast_dependency_map.py 스크립트를 호출하여
    direct_importers와 test_files를 수집한다.

    Args:
        affected_files: 영향받는 파일 경로 리스트
        workspace_root: 워크스페이스 루트 경로 (Path 또는 str)

    Returns:
        {
            "direct_importers": [...],  # 직접 임포터 목록 (중복 제거)
            "test_files": [...],        # 관련 테스트 파일 목록 (중복 제거)
            "total_affected": int,      # 총 영향 파일 수
        }
    z1/home/jay/workspace/scripts/ast_dependency_map.pyz.pyr   )rB  rC  total_affectedr   r;  r<  Tr=  r>  z[ast-blast-radius] u    분석 실패 (returncode=r   NrA  blast_radiusrB  rC  u)    분석 timeout(30s) 초과 — 건너뜀u    JSON 파싱 실패: u    예외 발생: )r!   endswithr   r   rw   r   rD  rE  r   r   r   r$   rF  r   r   extendrG  r   r   ry  r  r  r  )r8  r   rH  rB  rC  r1   py_filesfpath	fpath_strroot_dirfilenamer   r   blastr   s                  r2   _get_ast_blast_radiusr    s3   " EJJ)DaSV__U-CDHD$&bANN PJ	tI--.	?''	P^^J(IxP#	F   A%)(3NvO`O`Naadekererswtwexdyz ::fmm,DHH^R0E##EII.@"$EFeiib9:'P8 DMM*:;<dmmJ/0J - ./#j/A G E0 (( 	fNN0
:cde## 	UNN0
:OPQsSTT 	PNN0
:J1#NOO	Ps<   F/F/>A#F4"A3F44,I#I5HI"IIbatch_idc                    t         dz  dz  }|j                         sdddg dS 	 t        |dd      5 }t        j                  |      }d	d	d	       j                  di       }|j                         D ci c]  \  }}|j                  d      | k(  s|| }}}t        |      }	|	dk(  rdddg dS d}
g }|j                         D ]2  \  }}|j                  dd      }|dv r|
dz  }
"|j                  |       4 |
|	k(  }||	|
|dS # 1 sw Y   xY w# t
        $ r)}t        j                  d
|        dddg dcY d	}~S d	}~ww xY wc c}}w )u  task-timers.json에서 batch_id가 일치하는 모든 task의 완료 여부를 반환한다.

    Returns:
        {
            "complete": bool,   # total > 0이고 모두 done/completed인 경우 True
            "total": int,       # 해당 batch_id task 수
            "done": int,        # done 또는 completed 상태 수
            "pending": list[str],  # 미완료 task id 목록
        }
    rJ   r   Fr   )completetotaldonependingr   r   r   Nu9   [check_batch_completion] task-timers.json 읽기 실패: r   r  r   r   )r  	completed   )r   r   r   r$   r   r   r   r   r   r   r  r   )r  r   r1   r   r   r   r   r   matchedr  
done_countr  r   r  s                 r2   check_batch_completionr  B  sp    X%(::J!AqRHHI*cG4 	 99Q<D	  HHWb!E27++-d$488JCW[cCcw}dGdLEz!AqRHHJG  $(B'**!OJNN7#$ U"H 5*QXYY/	  	  IRSTRUVW!AqRHHI
 es@   D D	D 6EEDD 	EE :E E>
   app.pyasgi.pymain.pywsgi.py	celery.py	config.py	manage.py	server.pydispatch.pysettings.py)u   아키텍처u   리아키텍처u   전체 구조architecturec                     t         D ]  }|| v sdd| dfc S  |D ](  }t        |      j                  }|t        v s!dd| fc S  t	        |      }|dk\  rdd| dfS |dk\  rdd| d	fS y
)u  task_desc와 affected_files를 분석하여 권장 레벨과 이유를 반환한다.

    우선순위:
    1. 고레벨 키워드('아키텍처' 등) → (3, 이유)
    2. 핵심 파일 포함(server.py 등) → (2, 이유)
    3. affected_files >= 5 → (3, 이유)
    4. affected_files >= 3 → (2, 이유)
    5. 그 외 → (1, "")

    Returns:
        (level: int, reason: str)
    r	  'u   ' 키워드 감지r   u   핵심 파일 포함: r  zaffected_files u   개로 Lv.3 이상 권장u   개로 Lv.2 이상 권장)r  r   )_HIGH_LEVEL_KEYWORDSr   rw   _CORE_FILESr  )r)  r8  keywordr1   r  ns         r2   _estimate_task_levelr    s     ( 8i7)#56778
  <7<<{"/z:;;< 	NAAv_QC'@ABBAv_QC'@ABBr?   c                 h    t        j                  d|       }|rt        |j                  d            S y)uu   task_desc에서 Lv.{숫자} 패턴을 찾아 정수로 반환한다.

    없으면 기본값 1을 반환한다.
    z	Lv\.(\d+)r  )r0  searchintgroup)r)  matchs     r2   _parse_task_levelr    s-    
 IIlI.E5;;q>""r?   levelskip_meetingc                    dddd}|j                  |d      }|dk  ryt        j                  dt        j                        }|j	                  |      sy|rt
        j                  d|         yt        dz  d	z  d
|  dz  }|j                         s-d|  d|j                   d}t
        j                  d|        |S 	 |j                  d      g d}	t        fd|	D              }
dv xs dj                         v }|
s|s,d|  d|j                   }t
        j                  d|        |S 	 y# t        $ r"}t
        j                  d|        Y d}~yd}~ww xY w)u  Lv.4 작업의 Agent 미팅 만장일치 결과 파일 존재 여부를 검증한다.

    Lv.4 판정: level이 'critical' 또는 'security'이고, task_desc에 Lv.4 패턴이 포함.
    미팅 파일 경로: memory/meetings/agent-meeting-{task_id}.md
    파일이 있으면 '만장일치' 또는 'unanimous' 키워드 포함 여부도 체크.
    부정 패턴('만장일치 실패', 'unanimous not', 'not unanimous')은 제외.

    Returns:
        WARNING 메시지 문자열, 또는 None (체크 통과/대상 아님)
    r   r	     normalcriticalsecurityNu%   (?:Lv\.4|레벨:\s*4|레벨:\s*Lv\.4)uB   [agent-meeting] --skip-meeting 플래그로 미팅 체크 스킵: rJ   meetingszagent-meeting-.mdu   ⚠️ Lv.4 작업(u1   )에 Agent 미팅 결과 파일이 없습니다: u,   . --skip-meeting 플래그로 스킵 가능.z[agent-meeting] r   r   )u   만장일치 실패zunanimous notznot unanimousu   만장일치하지c              3   &   K   | ]  }|v  
 y wr<   r=   )r  negr~   s     r2   r  z'_check_agent_meeting.<locals>.<genexpr>  s     Gc3'>G   u   만장일치	unanimousuE   ) 미팅 파일에 만장일치 합의가 확인되지 않습니다: u-   [agent-meeting] 미팅 파일 읽기 실패: )r   r0  compile
IGNORECASEr  r   r   r   r   rw   r   	read_textanyr  r,   )r   r)  r  r  _level_to_int_meetingdispatch_levellv4_patternmeeting_filemsgnegative_patternshas_negativehas_unanimousr   r~   s                @r2   _check_agent_meetingr    s    ()aQG*..ua8N **Er}}UKi( XY`Xabc x'*4yPS7TTL !'*[\h\m\m[n o9 : 	 	)#/0
L(('(:kG5FGG&'1S[GMMO5S}%gY.s  uA  uF  uF  tG  H  NN-cU34J  -   LFqcJKKLs   A1D6 6	E!?EE!zOptional[Path]c                    ddl }ddl}ddlm} |j	                  d|       st
        j                  d|        yt        dz  dz  dz  }|| z  }t        t        j                  j                  t        |                  }t        t        j                  j                  t        |                  }	 |j                  |       t        dz  dz  dz  }	g d}
|j                         j!                         }	 |j#                  ddd       |
D ]  \  }}||z  }|j)                         rt
        j+                  d|        4|	|z  }|j)                         st
        j                  d|        b	 |j-                  d      }|j/                  d|       j/                  d|      }|j1                  |d       t
        j+                  d|         |S # t        $ r t
        j                  d	| d
| d       Y yw xY w# t$        $ r%}t
        j'                  d| d|        Y d}~yd}~ww xY w# t$        $ r&}t
        j'                  d| d|        Y d}~Ld}~ww xY w)u  작업 3문서(계획서/맥락노트/체크리스트)를 자동 생성한다.

    템플릿 파일을 복사하고 YAML의 {task_id}, {date} 플레이스홀더를 치환한다.
    파일이 이미 존재하면 개별 파일 단위로 스킵(덮어쓰기 금지).

    Args:
        task_id: 작업 ID (예: task-1872, task-1872.1, task-1872_6.2)
        level:   작업 레벨 정수 (3 이상일 때만 호출됨)

    Returns:
        생성된 디렉토리 Path, 또는 검증 실패/에러 시 None
    r   N)datez^task-[\d._]+$u?   [3docs] task_id 검증 실패 (허용 패턴: ^task-[\d._]+$): rJ   plansr   u   [3docs] path traversal 감지: u    는 u    하위가 아님prompts	templatesz	task-docs))zplan.template.mdzplan.md)zcontext-notes.template.mdzcontext-notes.md)zchecklist.template.mdzchecklist.mdTi  )r   r   modeu$   [3docs] 디렉토리 생성 실패: : u   [3docs] 이미 존재, 스킵: u!   [3docs] 템플릿 파일 없음: r   r   z	{task_id}z{date}u   [3docs] 생성: u   [3docs] 파일 쓰기 실패: )r0  shutilr   r  r  r   r   r   r   r"   r   realpathr!   relative_to
ValueErrortoday	isoformatr   r,   r   r   r   r  r)   
write_text)r   r  _re_shutil_date
tasks_root
target_dirreal_target	real_roottemplate_dirr  r  exc	tmpl_nameout_nameout_path	tmpl_pathr~   s                     r2   _create_task_docsr    sV    & 99&0YZaYdef X%/'9Jg%Jrww''J89KRWW%%c*o67I	* y(;6DLI KKM##%E$EB
  ) M	8)??LL:8*EF 9,	!NN>ykJK	M))7);Gook7;CCHeTG':LL+H:67M" K  8U9+Ufgh   ;K=3%PQ$  	MLL9(2cUKLL	MsC   1G 4G; -AH,%G87G8;	H)H$$H),	I5IIc                  6   t         rt        	dddg g ddS t        t        t                    } | j	                         }t
        j                  d|j                  dd      t        |j                  dg             t        |j                  d	g                    |S )
u$  모든 running 세션의 토큰 사용량 체크 및 자동 대응.

    SessionResilience를 사용하여:
    - 70% 이상: WARNING 이벤트 기록
    - 85% 이상: CRITICAL 이벤트 + 세션 요약 저장 + resume 트리거

    Returns:
        check_all_sessions()의 결과 dict
    unavailableu7   SessionResilience 모듈을 사용할 수 없습니다.r   )r   rf  checkedrR  	criticalsnormalsr   u;   세션 체크 완료: checked=%d, warnings=%d, criticals=%dr  rR  r   )	_SESSION_RESILIENCE_AVAILABLE_SessionResiliencer!   r   check_all_sessionsr   r   r   r  )
resiliencer   s     r2   check_sessionsr  /  s     ),>,F#P
 	
 $3y>BJ**,F
KKE

9a FJJz2&'FJJ{B'(	 Mr?   bot_key_hashc                     | sy	 t        j                  ddd|  gddd      }|j                  dk(  S # t        $ r Y yw xY w)	uM  봇의 cokacdir 프로세스가 실행 중인지 확인.

    pgrep -f로 'cokacdir.*{key_hash}' 패턴을 검색하여
    해당 봇의 cokacdir 프로세스 존재 여부를 판별한다.

    Args:
        bot_key_hash: 봇의 키 해시 (BOT_KEYS 값)

    Returns:
        프로세스 존재 시 True, 미존재 시 False
    Fpgrepz-fz
cokacdir.*Tr]  r>  r   )r   rD  rE  r   )r  r   s     r2   _check_bot_processr
  N  s\     
dj78	
   A%% s   -3 	??delay_secondsc                 f    t        j                         t        |       z   }|j                  d      S )u9   현재 시간 + delay_seconds 후의 절대 시간 반환)secondsz%Y-%m-%d %H:%M:%S)r   r  r   r  )r  r  s     r2   get_dispatch_timer  j  s'    =99A::)**r?   r   c                    | j                         sy	 t        | d      5 }t        j                  |      }ddd       t        j                  di       j                               }|syg }|D ]o  }|j                  dd      }t        j                  d|      }|r*|j                  t        |j                  d                   Wt         j#                  d	| d
       q |syt%        t'        |            }	|	d   }
t)        dt+        |	            D ]  }|	|   |	|dz
     z
  dk\  r |
dz   S |	|   }
  |
dz   S # 1 sw Y   xY w# t        j                  t
        f$ r}t        d|       d}~ww xY w)uD   task-timers.json에서 다음 ID 계산 (이상치 필터링 적용)r  r   Nu5   task-timers.json이 손상됨. 수동 복구 필요: r   task-r   ^(\d+)u   task ID 파싱 실패 ()r     )r   r   r$   r   r   r,   r   ry  r   r   r)   r0  r  r   r  r  r   r   rQ  r   ranger  )r   r1   r   r   rJ  numsr  basemsorted_numsfiltered_maxis               r2   _compute_next_id_from_timersr  p  s   X*c" 	 a99Q<D	 
 DHHWb)..01HD ;yy"%HHY%KKAGGAJ(NN4QCq9:;  T#Kq>L1c+&' &q>KA..$6 ! #1~&
 !=	  	   '* XRSTRUVWWXs-   E D6E 6E ;E E/E**E/c                  ~   t         dz  dz  } t         dz  dz  }t         dz  dz  }|j                  j                  dd       t        |d      }	 t	        j
                  |t        j                         d}|j                         rR	 t        |j                         j                               }|d	k  r"t        j                  d
|       t        |       }nd}nt        |       }|rKt        |       }||k  rt        j                  d||       |}n!||z
  dk\  rt        j                  d||       |}d| }|j!                  t#        |dz                | j                         r,	 t        | d      5 }t%        j&                  |      }	ddd       ndi i}	d	vri |	d<   dt+        j,                         j/                         d|	d   |<   | j                  j                  dd       	 t        | d      5 }t%        j0                  |	|dd       ddd       |t	        j
                  |t        j4                         |j7                          S # t        t        f$ r$ t        j                  d       t        |       }Y w xY w# 1 sw Y   xY w# t$        j(                  t        f$ r'}
t        j                  d|
        di i}	Y d}
~
3d}
~
ww xY w# 1 sw Y   xY w# t        $ r"}
t        j3                  d|
        Y d}
~
d}
~
ww xY w# t	        j
                  |t        j4                         |j7                          w xY w)uT   자동 태스크 ID 생성 (카운터 파일 기반, 파일 락으로 중복 방지)rJ   r   .task-counterr   Tr   r   Fr   uB   카운터 파일 값 비정상 (%d), task-timers.json에서 복구u6   카운터 파일 손상, task-timers.json에서 복구u2   카운터(%d) < timers 최대(%d), 보정합니다r  uW   카운터(%d)가 timers 최대(%d) 대비 1000 이상 큼, 이상치로 보정합니다r  r  r   Nu.   task-timers.json 재읽기 실패, 초기화: r   reserved)r   reserved_atr   r   u    task-timers.json 쓰기 실패: )r   r   r   r   r   r   r   r   r  r  r/  r   r   r  r  r,   r  r!   r$   r   r   r   r  r  r%   r   r   r   )r   counter_filer   	lock_filefrom_counternext_num
timers_maxnext_idr1   
timer_datar   s              r2   generate_task_idr'    s   X%(::Jx'/9L),??Nt<^S)I<Iu}}-  	D|557==?@q=NN#giqr;JGH#'L 4J?H 5jAJ*$SU]_ij%J&$.mow  zD &($ 	HqL 12 +*c* .a!%1J. "2J*$"$Jw2<X\\^MeMeMg'h
7G$t<	Aj#& G!		*aeAFG
 Iu}}-_ ( DWX7
CD6. .(('2 +!OPQsST%r]
+G G 	ALL;A3?@@	A
 	Iu}}-s   6L AI BL "J	 .I=J	 AL #K /K	K L /I:6L 9I::L =JJ	 	K	"K>L K		L KK 	L!K>9L >LL 6L<c                    t         dz  dz  }t         dz  dz  }| j                  dd      }t        j                  d|      }|syt	        |j                  d            }t        |d	      }	 t        j                  |t        j                         d
}|j                         r(	 t	        |j                         j                               }||k\  r8|j                  t!        |dz                t"        j%                  d||dz   |        t        j                  |t        j&                         |j)                          y# t        t        f$ r d
}Y w xY w# t        j                  |t        j&                         |j)                          w xY w)uS   외부 지정된 task_id가 카운터보다 크면 카운터를 업데이트한다.rJ   r  r   r  r   r  Nr  r   r   u2   카운터 동기화: %d → %d (외부 task_id=%s))r   r)   r0  r  r  r  r   r   r   r   r   r  r/  r  r,   r  r!   r   r   r   r   )r   r   r   r  r  	given_numr!  currents           r2   _sync_counter_if_neededr+    s@   x'/9L),??N??7B'DHHY%EEKKN#I^S)IIu}}- l446<<>? ##C	A$67KKLgW`cdWdfmnIu}}- (  	Iu}}-s0   +6E "'D; 	=E ;EE EE 6Fbase_task_idforcec                    | j                  d      s	dd|  ddS t        t              dz  dz  |  dz  }|j                         sdd	| dS t        t              dz  d
z  |  dz  }|j                         rdddS t	        j
                  dd|       }t        t              dz  d
z  |  dz  }d}|j                         r*	 t        |j                  d      j                               }t        t              dz  dz  }	d}
|	j                         rt	        j                  dt	        j                  |       d      }|	j                         D ]B  }|j                  |j                        }|s!t        |j!                  d            }||
kD  sA|}
D t#        ||
      dz   }|dk\  r|s	dd| ddS | d| }t        t              dz  dz  | dz  }|j                         s|}t        t              dz  dz  | dz  }	 |j                  d      }d|  d| d| d}|j%                  ||z   d       t        t              dz  d
z  | dz  }	 |j&                  j)                  d d !       |j%                  t+        |      d       d#||d$S # t        t        f$ r d}Y w xY w# t        $ r}dd| dcY d}~S d}~ww xY w# t        $ r"}t,        j/                  d"|        Y d}~bd}~ww xY w)%u   --resume 옵션 처리: base task 상태 확인 + 자동 채번 + task 파일 복사.

    Returns:
        성공: {"status": "ok", "new_task_id": "task-XXXX+N", "retry_count": N}
        실패: {"status": "error", "message": "..."}
    r  r   u#   잘못된 task ID 형식입니다: u    (task- 접두사 필요)r   rf  rJ   r   r  u*   task 파일이 존재하지 않습니다: eventsz.doneu=   이미 완료된 작업입니다. 새 task로 위임하세요\+\d+$r   z.retry_countr   r   r   ^z\+(\d+)\.md$r  r	  uE   3회 이상 재시도. 계속하려면 --force 추가 (현재 retry #r  +u   > **재시도**: r   	 (retry #u7   )
> **재시도 사유**: 이전 세션 실패/중단

u   task 파일 복사 실패: NTr   u"   retry_count 파일 쓰기 실패: ok)r   new_task_idretry_count)r  r   r   r   r0  r   r  r  r/  r  r,   r  escapeiterdirr  rw   r  maxr  r   r   r!   r   r   )r,  r   r)  r-  	task_file	done_filebase_without_plusretry_count_filefile_retry_count	tasks_dirmax_sibling_nsibling_patternr1   r  r  new_retry_countr6  origin_task_filenew_task_fileoriginal_content
retry_metar   new_retry_count_files                          r2   _resolve_resumerI    s    ""7+!0ST`Saaz.{|| Y(*W4,s7KKI!0Z[dZe.fgg Y(*X5<.8NNI!.mnn y"l; I1H<,|?\\ 	!"#3#=#=w#=#O#U#U#WX
 Y(*W4IM**"))4E*F)G|%TU""$ 	&A%%aff-A
O}$$%M	& *M:Q>O !E!0u  wF  vG  GH  /I  J  	J ''q(9:K I1G;AR@SSV>WW""$$Oh.8k]#;NNMQ+55w5G~U;-yHY ZF G 	 	  .>!> Q
  	?X5@k]R^C__A##))$)F''O(<w'O
 ;WWi G$ 	! 	!P  Q!0KA3.OPPQ  A;A3?@@AsH   )J 4J* 9K J'&J'*	K3K :K K	K3K..K3
project_idchain_id	task_typec           	      |    | t         vr#t        d|         t        j                  d       t	        | ||||||      S )uA   팀장에게 전달할 프롬프트 생성 (공통 모듈 위임)u   Error: 알 수 없는 팀 ID: r  rJ  rK  rL  )r4   printsysexit_build_team_prompt)r   r)  r   r  rJ  rK  rL  s          r2   r5   r5   P  sD     i.wi89)UzH`i r?   c                    t         dz  dz  |  dz  }|j                         st        j                  d|        y|j	                  d      }t        |d      }	 t        j                  |t        j                         t        |dd	
      5 }t        j                  |      }ddd       j                  dd      }|t        |j                  dg             k  re|d   |   }	|	d   D ]U  }
|
d   |k(  s|
d   dv s|
j                  d      &||
d<   t        j                         j                         |
d<   d|
d<    n t        |dd	
      5 }t        j                   ||dd       ddd       t        j#                  d|  d| d|        t        j                  |t        j(                         |j+                          y# 1 sw Y   "xY w# 1 sw Y   ixY w# t$        $ r"}t        j'                  d|        Y d}~td}~ww xY w# t        j                  |t        j(                         |j+                          w xY w)ug   chain.json 파일에서 해당 팀의 pending task에 task_id, dispatched_at, status=in_progress 기록rJ   chainsz.jsonu   chain 파일 없음: Nz.lockr   r   r   r   current_phase_idxr   phasesr   r   r   )r  in_progressr   dispatched_atrW  Fr   r   u   chain 업데이트: chain=, team=
, task_id=u   chain 업데이트 실패: )r   r   r   r   with_suffixr   r   r   r   r$   r   r   r  r   r  r  r%   r   r   r   r   r   )rK  r   r   
chain_file	lock_pathr   r1   r   current_idxphasetaskr   s               r2   _update_chain_taskra  b  s   X%0hZu3EEJ.zl;<&&w/I9c"GGU]]+*cG4 	 99Q<D	  hh2A6TXXh344N;/Eg 	LG+X*DD+3&-DO,4LLN,D,D,FD)%2DN	 *cG4 	=IIdAE!<	=0
''*U\T]^_ 	GU]]+/	  	  	= 	=  821#6778 	GU]]+sn   2G G"AG 3G ;G A G G'&G GG GG 	H$H<H	 HH	 	6H?c                 B   t         dz  dz  }t         dz  dz  }|j                  j                  dd       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i       }|j                  |       }|St        j                  d|  d       	 |6	 t	        j
                  |t        j                         |j                          yy|j                  d      }|dvrVt        j                  d|  d| d       	 |6	 t	        j
                  |t        j                         |j                          yy|| = ||d<   t        |dd	
      5 }t        j                   ||dd       ddd       t        j#                  d|  d| d       |6	 t	        j
                  |t        j                         |j                          yy# t        $ r Y yw 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# 1 sw Y   xY w# t        $ r%}	t        j%                  d|  d|	        Y d}	~	d}	~	ww xY w# t        $ r Y yw xY w# |E	 t	        j
                  |t        j                         |j                          w # t        $ r Y w w xY ww xY w)uO  task-timers.json에서 해당 task_id의 reserved 또는 running 엔트리 삭제.

    dispatch() 실패 시 호출하여 orphan 항목을 정리한다.
    task-timer 자동 시작 도입으로 reserved뿐 아니라 running 상태도 정리 대상.
    에러 발생 시 로깅만 하고 무시 (본래 에러 반환이 우선).
    rJ   r   r   Tr   Nr   r   r   r   r   z_cleanup_task: task_id u    없음, 정리 불필요r   )r  r   z_cleanup_task: u$    상태가 정리 대상이 아님 (u   ), 건너뜀Fr   r    u    엔트리 삭제 완료u   _cleanup_task 실패 (task_id=r   )r   r   r   r   r   r   r   r   r   r   r   r$   r   r   r   r   r%   r   r   )
r   r   r   r   r1   r&  r   r   r   r   s
             r2   _cleanup_taskrd    s    X%(::J),??Nt<G&~s+GU]]+  "8 GU]]3 5 *cG4 	&1J	& w+YYw'
LL27);TUV$ GU]]3 ! )00LL?7)3WX^W__klm GU]]3  'N#
7*cG4 	CIIj!%B	C 	ogYax7OPQ
 GU]]3   =	& 	&<  9 	C 	C
  G5gYc!EFFG  	 GU]]3 	 s   A J =4I 3J IAJ !4I+ 1J 4I: J J	2$J 4K 	III(#J +	I76I7:	JJ	JJ 	KJ>9K >KK 	KKL4LL	LLLLc                 f    t        |       dkD  r#t        j                  dt        |        d       yy)uR   지시서 크기가 3000자 이상이면 Phase 분리 권고 WARNING 로그 출력i  u#   [large-task-desc] 지시서 크기 u   자 (3000자 초과). Phase 분리를 권장합니다. 한 세션에서 모든 작업을 처리하면 성능이 저하됩니다.N)r  r   r   )r)  s    r2   _warn_large_task_descrf    s8    
9~1#i.1A By y	
 r?   generated_idc                 t    ||k7  ryt        j                  d|       rt        j                  d| d|        yy)u  Phase 작업 감지 시 --task-id 미지정 경고.

    task_desc에서 Phase 패턴(Phase N, phase N, Phase N.N)을 감지하고,
    task_id가 자동 생성(generated_id와 동일)이면 WARNING 로그를 출력한다.
    작업을 차단하지 않는다 (경고만).
    Nz(?<![a-zA-Z])[Pp]hase\s+\duT   ⚠️ Phase 작업 감지됨. --task-id를 수동 지정하세요. (예: --task-id u   _N.N) 자동 생성 ID: )r0  r  r   r   )r)  r   rg  s      r2   _warn_phase_without_task_idri    sH     ,	yy.	:&i (!!(	+	
 ;r?   c           
         t        j                  t        t                    dz   }t        j                  ||       }g }d}g }|D ]  }t
        j                  j                  |      s#	 t
        j                  j                  |      }||z  }|dkD  sNt
        j                  j                  |      }t        |dz  d      }	t        |dz        }
|j                  ||	|
f       |j                  d| d|	 d	|
 d
        t        |dz  d      }|t        kD  r!|j                  dd| dt        dz   d       n,|t        dz  kD  r |j                  dd| dt        dz   d       |rdj                  |      S dS # t        $ r Y Aw xY w)uh   task_desc에서 참조 파일을 추출하여 대용량 파일 경고 + 총 크기 한도 경고 반환.z[a-zA-Z0-9/_.\-]+r   ia  i   r  g      @u    ⚠️ 대용량 참조 파일:  (zKB, ~u#   tok) → offset/limit 사용 필수u   🚨 참조 파일 총 크기 u   KB (한도: ug   KB 초과!) — 전체 읽기 금지. 반드시 offset/limit 분할 읽기 또는 요약 파일 참조.gffffff?u    ⚠️ 참조 파일 총 크기 uD   KB의 70% 초과) — offset/limit 사용을 강력 권장합니다.r[  N)r0  r8  r!   r   r  r"   r   isfilegetsizer,   basenameroundr   MAX_REF_FILE_TOTAL_BYTESinsertrP  )r)  patternpathsrR  
total_sizelarge_filesr   sizer  size_kbestimated_tokenstotal_kbs               r2   _check_referenced_file_sizesrz    s   iiI'*>>GJJw	*EHJK ww~~d#	77??4(D 	d
%<ww''-HD4K+G$TCZ0'3CDEOO28*BwiuM]L^  _B  C$ Z$&*H,,,XJlC[_cCcBd ej k	

 
.4	4.xjE]aeEeDf gA B	
 #+499X447  		s   (E99	FFc                    | dk(  ry| j                  d      sy| j                  dd      }t        dz  |z  dz  dz  }|j                         sd	|  d
S t        j
                  j                  t        |            sd	|  dS t        j
                  j                  t        |            }t        j
                  j                  ddd      }|j                  |      s	d	|  d| dS y)u   팀의 QC 환경(verifiers symlink) 사전 검증.

    dev8은 독립 구조 허용으로 예외.
    비정상이면 WARNING 메시지 반환 (블로킹 아닌 경고).
    2026-04-17 에이전트 미팅 합의 항목 3.
    rm   Nr
  r  r   rd   rz   	verifiersu   ⚠️ QC 환경 경고: u*   의 verifiers 디렉토리가 없습니다uY   의 verifiers가 symlink가 아닙니다. shared/verifiers로의 symlink여야 합니다.shareduP   의 verifiers symlink가 shared/verifiers를 가리키지 않습니다 (실제: r  )r  r)   r   r   r"   r   islinkr!   r  rP  r  )r   
team_shortverifiers_path	real_pathexpected_suffixs        r2   _check_team_qc_envr     s     + e$"-J(:5<{JN  "*7)3]^^77>>#n-.'y  1N  O	
   ^!45Iggll7HkBOo.'y 1FFO[PQS	

 r?   	thresholdc                    g }| D ]  }|}t         j                  j                  |      s-t         j                  j                  t	        t
              |      }t         j                  j                  |      sq	 t        |ddd      5 }t        d |D              }ddd       |kD  r|j                  ||d        |S # 1 sw Y   &xY w# t        $ r Y w xY w)u   affected_files 중 threshold줄을 초과하는 대형 파일 목록 반환.

    Returns:
        list of dict: [{"path": "...", "lines": 3036}, ...]
    r   r   ignore)r   errorsc              3       K   | ]  }d   ywr  Nr=   )r  r  s     r2   r  z#_get_large_files.<locals>.<genexpr>4  s     qs   N)r   r1  )r"   r   isabsrP  r!   r   rl  r   sumr   r,   )r8  r  largefilepathresolvedr1   
line_counts          r2   _get_large_filesr  %  s     E" ww}}X&ww||C	NH=Hww~~h'	hghG /1 A.
/I%hDE L/ /  		s*   7C
B>!C
>C	C

	CCc                      j                         g d}d}dv xs t        t        j                  d|              xsg t        t        j                  | d             xsC t        t        j                  d             xs" t        t        j                  d|             }|rt	         fd|D              rd	}|sy
t
        dz  dz  }|j                         st        j                  d|       y
	 |j                  d      }t        j                  d       |S # t        $ r }t        j                  d|       Y d
}~y
d
}~ww xY w)u=  지시서 키워드 기반 플랫폼 규칙 자동 주입.

    네이버 블로그 관련 키워드가 감지되면 config/naver-blog-rules.md 내용을 반환.
    향후 티스토리 등 다른 플랫폼 규칙도 동일 패턴으로 확장 가능.

    Returns:
        주입할 규칙 텍스트 또는 None
    )u   검증u   폐기u   리팩토링u   마이그레이션u   삭제
deprecatedu   코드 검토u   셀프체크u	   일원화verifyrefactoru   테스트 실행u   코드 분석u   버그 수정u	   디버깅r   u   코드 리뷰uM   (작성|발행|글\s*쓰기|글쓰기|포스팅|올리기|publish|write|post)zblog-publish-naveru   네이버\s*블로그.{0,20}u   .{0,20}네이버\s*블로그u%   네이버.{0,10}(블로그\s*)?발행znaver.{0,10}blog.{0,20}c              3   2   K   | ]  }|v xs |v   y wr<   r=   )r  kwr)  
task_lowers     r2   r  z)_inject_platform_rules.<locals>.<genexpr>l  s"     "c22?#FbJ6F#F"cs   FNr  znaver-blog-rules.mdu+   [naver-blog-rules] 규칙 파일 없음: %sr   r   uB   [naver-blog-rules] 네이버 블로그 규칙 자동 주입 완료u2   [naver-blog-rules] 규칙 파일 읽기 실패: %s)r  boolr0  r  r  r   r   r   r   r  r   r,   )r)  _EXCLUDE_KEYWORDS_WRITE_INTENTnaver_blog_detected
rules_pathrules_contentr   r  s   `      @r2   _inject_platform_rulesr  <  sO    "J* eM 	
* 	W		;M?KYWX	W 		m_,JKYWX		W 		BINO	W 		8H*UV  s"cQb"cc# X%(==JDjQ",,g,>XY KQOs   :(D# #	E,EEc                      g d}g d}t         fd|D              }t         fd|D              }|r|r|dk7  rt        j                  d       yyyy)uB   리서치+구현 혼합 지시서 감지 시 WARNING 로그 출력)u	   리서치u   조사u   파악u   현재 상태u   가능한지u   방법 조사u
   API 문서u   HTML 구조u	   셀렉터u   외부 서비스u   인증 방식2FAOTPresearchinvestigatefeasibility)
u   구현u   구축u   코딩u   코드 작성u   테스트 작성u   파이프라인	implementbuild	PublisherPipelinec              3   &   K   | ]  }|v  
 y wr<   r=   r  r  r)  s     r2   r  z*_warn_research_impl_mix.<locals>.<genexpr>  s     C2rYCr  c              3   &   K   | ]  }|v  
 y wr<   r=   r  s     r2   r  z*_warn_research_impl_mix.<locals>.<genexpr>  s     ;r2?;r  r  u   [research-impl-mix] 지시서에 리서치와 구현이 혼합되어 있습니다. Phase 분리를 권장합니다. (specs/research-impl-separation.md 참조) 세션 경량화: 리서치 후 /compact 실행 후 구현에 진입하세요.N)r  r   r   )r)  rL  research_keywordsimpl_keywordshas_researchhas_impls   `     r2   _warn_research_impl_mixr    s\    $M C1BCCL;];;HY*%<]	
 &=|r?   c           	         t         dz  }|j                         st        j                  d       y	 |j	                  d      }|j                  d      D cg c]  }d|v s|j                          }}t               }d	}|D ]e  }	t        j                  d
|	      D ]J  }
|
|v rt         |
z  }|j                         s"	 |j	                  d       |j                  |
       |dz  }L g g d}|j                         }|D cg c]	  }||v s| }}t        |      }|rHd}||vrBt         |z  }|j                         r)	 |j	                  d       |j                  |       |dz  }d	}	 d	dlm}  |       }	 |j!                  |dd      }t#        |      }|j%                          	 t        j)                  dt#        |       d| d| d       | j+                  d      r|rt        j-                  d|  d|        yyy# t
        $ r"}t        j                  d|        Y d}~yd}~ww xY wc c}w # t
        $ r&}t        j                  d|
 d|        Y d}~d}~ww xY wc c}w # t
        $ r&}t        j                  d| d|        Y d}~.d}~ww xY w# |j%                          w xY w# t&        $ r#}t        j                  d|        Y d}~1d}~ww xY w)u   MEMORY.md ★ 항목 확인 및 디자인 작업 dev팀 위임 위반 감지.

    파일 I/O 실패 시 예외를 전파하지 않고 debug 로그만 남긴다.
    z	MEMORY.mdu5   [memory_check] MEMORY.md 파일 없음, 체크 스킵Nr   r   u(   [memory_check] MEMORY.md 읽기 실패: r[  u   ★r   z\[.*?\]\((.*?\.md)\)r  u,   [memory_check] 링크 파일 읽기 실패: u    – )		   디자인   배너	   이미지u	   포스터u   일러스트bannerimageposterr|   z"feedback_design_team_routing_v2.mdu/   [memory_check] 피드백 파일 읽기 실패: )MemoryIndexerr	  r|  )limitlayeru4   [memory_check] FTS5 Layer 1 검색 실패 (무시): u   [memory_check] ★ u   개 확인, 관련 피드백 u"   개 확인, FTS5 관련 메모리 u   건r
  uT   ⚠️ [memory_check] 디자인 작업을 dev팀에 위임하려고 합니다! team=u   , 감지 키워드: )_MEMORY_BASE_PATHr   r   r   r  r,   r  r/  r   r0  r  r   r  r  utils.memory_indexerr  r  r  r   r   r   r  r   )r   r)  memory_file	memory_mdr   r2  
star_itemsloaded_filesmatched_countr6  fnameref_pathdesign_keywordsr  r  detecteddesign_keywords_foundextra_fname
extra_path	fts_countr  _indexerfts_resultss                          r2   _check_memory_before_dispatchr    s   
 $k1KLM))7);	 ,5??4+@R4ETM$**,RJR LM aZZ 7> 
	aE$(50H a&&&8 $$U+!Q&M
	aa O"J,Arj0@AHA N :l**[8J  "j(('(: $$[1!Q&M
 I
Q6 ?	"//)1G/LKK(INN KK
c*o..KM? [##,+S	2 % %:efmen  oC  DL  CM  N	
 &; }  ?sCD
 S  aLL#OPUwV[\][^!_``a B  jLL#RS^R__defdg!hiij NN QKA3OPPQs   H 	H/ H/>(H4	I&I&?(I+ *J2 8J J2 	H,
H''H,4	I#=II#+	J4JJJ//J2 2	K;KKskip_brainstormingc                    |rt         j                  d       ydddd}|j                  |d      }|dk  ryg d}|j                         }|D cg c]  }|j                         |v s| }	}|	syt        dz  d	|  d
z  }
|
j                         rt         j                  d|
        yt         j                  d| d|	 d       yc c}w )u*  Lv.3+ UX 작업에 brainstorming 사전 실행 여부를 확인한다.

    skip_brainstorming=True이면 즉시 반환한다.
    level이 3 미만이면 즉시 반환한다.
    UX 관련 키워드가 없으면 즉시 반환한다.
    brainstorming 파일이 없으면 warning을 남긴다.
    u6   [brainstorming-gate] --skip-brainstorming으로 스킵Nr   r	  r  r  )UXUIr  u   사용자 경험u   인터페이스u   레이아웃u   화면u	   페이지u   컴포넌트u	   프론트frontendr  zbrainstorming-r  u5   [brainstorming-gate] brainstorming 파일 확인됨: u   ⚠️ [brainstorming-gate] Lv.u9    UX 작업에 brainstorming 미실행. 감지 키워드: uf   . /brainstorming 스킬 실행 후 재위임하세요. --skip-brainstorming 플래그로 스킵 가능)r   r   r   r  r  r   r   r   )r   r)  r  r  	level_map	level_intux_keywordsr  r  detected_keywordsr   s              r2   _check_brainstorming_gater    s     MN!;IeQ'I1}K "J&1NRXXZ:5MNNz)nWIS,IID{{}KD6RS
NN
)) 5./ 0o	p Os   C'Cc                     t         r*t        r$	 t        j                  d      } | rt        |       S 	 t
        r<t        6t        t              }|j                         }|j                  di       } | r| S t        dz  dz  }|j                         r?	 t        |dd      5 }t        j                  |      }ddd       j                  di       S i S # t        $ r Y w xY w# 1 sw Y   ,xY w# t        j                  t        f$ r Y i S w xY w)	uE   config/constants.json에서 logical_teams 도메인 매핑을 로드.logical_teamsNr   r  r  r   r   r   )_CONFIG_AVAILABLE_cfgget_constantr  r   r   r   r   _load_constantsr   r   r   r$   r   r   r,   )r   mgrr  r  r1   r   s         r2   _load_logical_teamsr  #  s    T	&&7FF|#  !2!>y9'')	3M ),<<N	ncG< $yy|$88OR00 I+  		 $ $ $$g. 	I	s;   !C C+ C4C+ 	CCC($C+ +DDc                 x    t         r%t        t        t              j                         S t	               }|syd}d}|j                         D ]b  \  }}|dk(  r|j                  dg       }|j                  dg       }t         fd|D              rEt         fd|D              }||kD  s_|}|}d |dkD  r|S dS )	u   task 설명에서 키워드 매칭 → 적합한 논리적 팀 추천.

    Returns:
        추천 팀 ID (예: "design") 또는 None (매칭 없음)
    Nr   r   	compositekeywordsanti_keywordsc              3   &   K   | ]  }|v  
 y wr<   r=   )r  akr)  s     r2   r  z _suggest_team.<locals>.<genexpr>\  s     72rY7r  c              3   ,   K   | ]  }|v sd   ywr  r=   r  s     r2   r  z _suggest_team.<locals>.<genexpr>`  s     <"B)OA<s   	)	r   r   r   suggest_teamr  r   r   r  r  )	r)  r  	best_team
best_scorer   r  r  r  scores	   `        r2   _suggest_teamr  C  s     !2!> 	:GG	RR ()M#IJ(..0  k!J3$jj"= 777 <<<:JI! $ #Q90D0r?   override_routingc                    t         rTt        Nt        t              j                  | ||      }|y|j	                  dd      }|j                  d      sd|z   }|S | t        v ryt        |      }|y|rt        j                  d|  d| d	       yt               }|j                  |i       j                  d
d      }d| d| dS )u  dev팀에 논리적 팀 소관 작업이 위임되면 WARNING 반환.

    Args:
        team_id: 위임 대상 팀 ID
        task_desc: 작업 설명
        override_routing: True이면 라우팅 경고를 무시

    Returns:
        경고 메시지 또는 None (문제 없음)
    Nr   u(   override_routing=True를 사용하세요u%   --override-routing을 추가하세요u   ⚠️u   ⚠️ z[routing-override] u   에 u1    소관 작업 위임 (--override-routing 적용)descriptionr   u   ⚠️ 이 작업은 --team u   이 적합합니다 (u9   ). 계속하려면 --override-routing을 추가하세요.)r   r   r   validate_routingr)   r  DYNAMIC_BOT_TEAMSr  r   r   r  r   )r   r)  r  r  	suggestedr  r  s          r2   _validate_team_routingr  i  s     !2!>y9JJ7T]_op;kkDFmn~~h'c/C
 ## i(I,WIT)Duvw')M##Ir266}bIK
&yk1F{m  TQ  	Rr?   	teams_strc                    | j                  d      D cg c]#  }|j                         s|j                         % }}t        |      dk  rt        d| d      t        |      t        kD  rt        dt         dt        |       d      |D cg c]  }|t
        vs| }}|rt        d| d	t        t
                     t        |      t        t        |            k7  rt        d
|       |S c c}w c c}w )u   composite teams 문자열 파싱 + 유효성 검증.

    Args:
        teams_str: 쉼표 구분 팀 ID 문자열 (예: "marketing,design")

    Returns:
        검증된 팀 ID 리스트

    Raises:
        ValueError: 유효하지 않은 입력
    r+  r   u<   composite는 2개 이상의 팀이 필요합니다 (입력: r  u   최대 u   개 팀까지 허용 (입력: u   개)u   알 수 없는 팀 ID: u   . 허용 목록(소문자): u   중복 팀 ID: )r  r/  r  r  r8   r7   rQ  r   )r  r  rd   r   s       r2   _validate_composite_teamsr    s     !* 4B1	QWWYBEB
5zA~WXaWddefgg
5z''7#6"77UVYZ_V`UaaefggDQ1,C#CqDGD27);WX^_vXwWxyzz
5zSU_$?5'233L C
 Es   C4C4C9C9composite_teamsc           
         d}|t               }|}nt        |       |t        |||       t        dj	                  |              t
        rst        m	 t        |      }|j                  sU|j                  D cg c]  }|j                  dv s| }	}|	r+t        |       dd|	D cg c]  }|j                   c} dS t        rIt         C	 d| }t!        |      }t        j#                  d	|j$                   d
|  d|j&                          |}|4t(        r.t*        (	 t+        |      }t        j#                  d| d
|         n|rt        j#                  d| d
|         t        j#                  d|  d|        |dd t-        |      dkD  rdndz   }dt/        t0              d|ddd|g}t3        j4                  |ddd      }|j6                  dk7  r3t        j9                  d| d|j:                  j=                                 t>        d z  d!z  }|jA                         r	 tC        |d"d#$      5 }tE        jF                  |      }ddd       jI                  d%i       jK                         D ]  \  }}|jI                  d&      d'k7  s||k(  r |jI                  d(d      }|jI                  d)g       }|rtM        |      n|h}|tM        |       z  }|se|st        |       dd*| d+| d,dc S t        j9                  d-| d.| d/        n tS        jT                  d1d2| d3|d45      }t>        d z  d%z  | d6z  }|jV                  jY                  dd7       |j[                  |d#$       t\        rt^        	 t_        |t/        |      d8d9:       	 dd<l0m1}  || |||      }d?d@dAdB}|jI                  |d?      }tf        r/th        )ti        |      r|dCz  }t        j#                  dD| dE       	 tk        ||F      } tn        |    }!tp        jI                  |!      }"|"Ht        j9                  dG|!js                          dH       t        |       ddI|!js                          dS tu        |d| | J       dK}#dLdM|dNtw        |#      dOtx        dP|"dQg
}$	 t3        j4                  |$ddd      }%|%j6                  dk(  rd	 tE        j|                  |%j~                        }&ddTl@mA}'  |'ddU       t                |dd t-        |      dkD  rdndz   }t        r	 t        |      }dVj	                  |       }(dW| dX|( dY| })t3        j4                  dt/        t0              dZ|)d[d8gddd       t        j#                  d\| d]|(        d^|d| d_||d`|( da|&db	}*t        |&t              r|&jI                  dc      nd}+|+r|+dd?dd| d6de},tu        |fi |, 	 ddflGmH}-  |-||      }.|.r>t        j#                  dg|.jI                  dh       di|        |.jI                  dh      |*dh<   |*S t        je                  dk|%j:                  j=                                 t        |       d|%j:                  j=                         dlj	                  |$ddA dmgz         dnS c c}w c c}w # t        $ r#}
t        j                  d|
        Y d}
~
d}
~
ww xY w# t        $ r#}
t        j                  d|
        Y d}
~
d}
~
ww xY w# t        $ r#}
t        j                  d|
        Y d}
~
d}
~
ww xY w# 1 sw Y   xY w# tD        jN                  tP        f$ r#}t        j9                  d0|        Y d}~[d}~ww xY w# t        $ r#}
t        j                  d;|
        Y d}
~
d}
~
ww xY w# t        $ r5}t        je                  d=|        t        |       dd>| dcY d}~S d}~ww xY w# tl        $ r#}t        |       dt/        |      dcY d}~S d}~ww xY w# t2        jz                  $ r( t        je                  dR       t        |       ddRdcY S w xY w# tD        jN                  $ r  dS|%j~                  j=                         i}&Y w xY w# t        $ r Y rw xY w# t        $ r#}/t        j9                  dj|/        Y d}/~/|*S d}/~/ww xY w)ou   복합업무 임시팀 디스패치.

    하나의 봇 세션에 여러 논리적 팀을 조합하여 Phase별로 순차 실행.
    임시팀장(Opus)이 Phase 관리, 핸드오프, 품질 검수를 일괄 수행.
    Nr+  highr  r   u)   보안 위협 감지 (composite 차단): r/  *   [injection_guard] 스캔 실패 (무시): z
composite [approval] risk_level=z, composite_teams=, patterns=#   [approval] 검사 실패 (무시):    [model_router] 권장 모델: *   [model_router] 라우팅 실패 (무시): (   [model_router] 명시적 모델 지정: u,   복합업무 위임 시작: composite_teams=rZ  <   ...r   r   start--teamr  --descTr=  r>  r   !   task-timer start 실패 (task_id=r   rJ   r   r   r   r   r   r   r   r   r  u   겹치는 팀(/   )에 이미 running 태스크가 있습니다: uB   . 완료 후 재시도하거나 --force로 강제 실행하세요.u   [force] 겹치는 팀(&   )에 이미 running 태스크 있음: u   . --force로 강제 진행.!   running 태스크 확인 실패: ^# task-\d+\.\d+:# :r  countr  r   dispatchwriter   r  tool	operation.   [audit_logger] 감사 기록 실패 (무시): )build_composite_promptu   build_composite_prompt 실패: u   프롬프트 생성 실패: r   r	  r  r    

## ⚠️ Sanitize 게이트 (Lv.3+)
외부 AI(Codex/Gemini) 호출 전 반드시 sanitize 검사를 수행하세요:
- 대상: affected_files의 코드 + 설계 문서
- 마스킹 항목: 주민등록번호, 연락처, API 키, 계좌번호, 보험 증권번호
- 방법: `from utils.sanitize_gate import sanitize_text` 사용
- PII가 마스킹된 코드만 외부 AI에 전달할 것
[sanitize-gate] Lv.u5    composite 작업에 sanitize 게이트 지시 삽입r      환경변수 COKACDIR_KEY_
    미설정6   봇 키가 설정되지 않았습니다: COKACDIR_KEY_)rx   r   r  r]  cokacdir--cron--at--chat--key--once+   cokacdir 호출 타임아웃 (60초 초과)rawset_bot_status
processingr3  [u   ] 복합업무 임시팀(   )에게 위임: log--typeu   복합업무 위임 완료: r   
dispatchedu   복합업무 임시팀장u   복합업무 임시팀(u@   )에게 위임 완료. 즉시 독립 세션에서 작업 시작.)	r   r   r   r  r   r  r  rf  cron_responserx  memory/tasks/)schedule_idr7  	max_retryr;  issue_mc   [memory-check] MC 발급: mc_id for *   [memory-check] MC 발급 실패 (무시): u   복합업무 위임 실패: rc     ...(생략)r   rf  command)Ir'  r+  ri  r  rP  _INJECTION_GUARD_AVAILABLE_scan_contentis_safethreatsseverityrd  pattern_namer   r   r   _APPROVAL_AVAILABLE_check_commandr   
risk_levelmatched_patterns_MODEL_ROUTER_AVAILABLE_route_modelr  r!   
TASK_TIMERr   rD  rE  r   r   r/  r   r   r   r$   r   r   r   r   r   r,   r0  r   r   r   r  _AUDIT_LOGGER_AVAILABLE_log_file_operationprompts.team_promptsr  r   _SANITIZE_GATE_AVAILABLE_should_sanitizer   r   
BOT_TO_KEYr   upperr(  r  r   rG  rF  r   utils.bot_activityr$  r$  _REDACT_AVAILABLEr>   
isinstancer  utils.memory_checkr0  )0r  r)  r  r   r-  r   rg  _scan_resultr  _high_threats_e_approval_input_approval_result_recommended_model
short_desc	timer_cmdtimer_resultr   r1   existing_dataexisting_tidexisting_taskexisting_teamexisting_compositeexisting_team_setrV  r   r;  r  prompt_composite_level_to_int_composite_dispatch_levelr   r   rL  _dispatch_delayrI  r   responser$  teams_labellog_msg_result_schedule_id_watchdog_metar0  	mc_result_mc_errs0                                                   r2   _dispatch_compositerk    si	    L"$ 	 ( #IwE chh78 "m&?	L(3L'',8,@,@ gqAJJRfDf g g !'*")%NhuOvcdPQP^P^OvNw#x  ~9		E *9+6O-o>KK()9)D)D(E F##2"3 4,==>@ &+!&=,BZ	L!-i!8KK89K8LL^_n^opq 
>?Q>RRdetduvw
KK>>OzZaYbcd 3BC	NR,?5RHJJ	I >>)DtUWXL!#:7)3|GZGZG`G`GbFcde X%(::J	Dj#8 -A $		!-/</@/@"/M/S/S/U +m $$X.);|w?V - 1 1)R @%2%6%67H"%M"?QC(:$;XeWf!+c/.BB %g.&-"0	9h#/.0r!t   NN0	9_'.(CE /8 +r'!_iqQI H$w.G9C@I4$7W5 #6#B	P#i.zelm
R?')US *+qI 7 ; ;E1 E$4$@EUVoEpK	
 	)*C)DDyz{6.wuM ,'H
,,x
 C
{3HNN4D3EZPQg!0fgogugugwfx.yzz 'WfgO 	/*C]DtRP A	6zz&--0H
 	6{L1 s^I0CuL
)*5
 hh/gY7}DTU_T`aJ:N		
 	27)5NO"./$0=}~%

 .8$-Gx||D)T+ ,WIS9	N "'<^<	S3 )4I8w9O8PPUV]U^_`#,==#9  3FMM4G4G4I3JKLg!fmm.A.A.CPSPXPXY\]_^_Y`dqcrYrPsttM !h
 Pw 	LLLEbTJKK	L  	ELL>rdCDD	E  	LLLEbTJKK	L6- -4 $$g. 	DNN>qcBCC	D  	PLLI"NOO	P  R6qc:;g!0LQC.PQQR,  6g!c!f556> $$ ]BCg!.[\\] ## 	6v}}2245H	6  V  	SNNGyQRR	SsM  %Z 8ZZZ %Z8Z A[	 &[8 %\4 3\'	B\4 \4 1\4 <]3 ^" 4_# ` >a b Ab 
Z 	[#[[		[5[00[58	\$\\$'\1,\4 4]0]++]03	^<^^"	_ +*__ _ #	`,`
`
`8aa/bb	bb	cb==cprd_contentc                    t        j                  dt         j                        }t        j                  dt         j                        }g }t        |j	                  |             }t        |      D ]  \  }}|j                  d      }t        |j                  d            }|j                  d      j                         }	|j                  d      j                         }
|j                         }|j                  | |      }|r|j                         n
t        |       }| || j                         }d}t        j                  dt         j                        }|j                  |      }|r|j                  d      j                         }g }t        j                  d	|
      }|r;|j                  d      j                  d
      D cg c]  }|j                          }}|j                  |||
|	|||d        |S c c}w )u!  PRD 문서에서 Phase/Sprint 섹션을 정규식으로 추출한다.

    Args:
        prd_content: PRD 파일 전체 텍스트

    Returns:
        phase 딕셔너리 리스트. 각 항목은 phase_number, phase_type, title,
        duration, body, features, dod 키를 가진다.
    u?   ^###\s+(Sprint|Phase)\s+(\d+)\s*\(([^)]+)\)\s*[-—–]\s*(.+)$z^###\s+(Sprint|Phase)\s+|^##\s+r  r   r	  r  Nz\*\*DoD\*\*:(.*?)(?=\n##|\Z)z\[([^\]]+)\]r+  )phase_number
phase_typetitledurationbodyfeaturesdod)r0  r  	MULTILINEry  finditer	enumerater  r  r/  endr  r  r  DOTALLr  r   )rl  header_patternnext_section_patternrV  matchesr  r  ro  rn  rq  rp  
body_startnext_mbody_endrr  rt  dod_patterndod_mrs  feat_mr1   s                        r2   _parse_prd_regexr  	  s    ZZJ
N ::&H",,WF>**;78G'" $
1WWQZ
1771:771:##%
  " UUW
%,,[*E%+6<<>[1A:h/557 "jj!@"))L""4(++a.&&(C !?E2+1<<?+@+@+EFa	FHF ,($$
	
5$
L M Gs   G=prd_pathc                    d| d}d}	 t        j                  |d|ddgdddd	
      }|j                  dk7  r2t        j	                  d|j
                  j                                 g S |j                  j                         }	 t        j                  |      }t        |t              r|S t        |t              r@d|v r<|d   }t        |t              rt        j                  |      S t        |t              r|S t        j                   d|t        j"                        }|r$t        j                  |j%                  d            S t        j	                  d       g S # t        j                  $ r Y ww xY w# t         j&                  $ r t        j	                  d       g cY S t(        $ r$}	t        j	                  d|	        g cY d}	~	S d}	~	ww xY w)u  claude CLI를 호출해 PRD에서 Phase/Sprint 정보를 JSON으로 추출한다.

    Args:
        prd_path: PRD 파일 경로 (프롬프트 참조용)
        prd_content: PRD 파일 전체 텍스트

    Returns:
        phase 딕셔너리 리스트. 실패 시 빈 리스트.
    u  다음 PRD 문서에서 구현 로드맵 섹션의 Phase/Sprint별 태스크를 JSON 배열로 추출해줘.

각 항목 형식:
{"phase_number": 0, "phase_type": "Sprint" 또는 "Phase", "title": "제목", "duration": "소요시간", "body": "항목 내용 전체", "features": ["F1", "F2"], "dod": "완료조건 또는 null"}

JSON 배열만 출력하세요. 다른 텍스트 없이.

--- PRD 문서 시작 ---
u   
--- PRD 문서 끝 ---z/home/jay/.local/bin/claudez-pz--output-formatr$   Tx   z/tmp)r?  r:   r@  cwdr   u    [handle_prd] claude CLI 오류: r   z\[.*\]uG   [handle_prd] claude 출력에서 JSON 배열을 찾지 못했습니다.z&[handle_prd] claude CLI timeout (120s)u'   [handle_prd] claude CLI 호출 실패: N)r   rD  rE  r   r   r   r/  r   r$   rF  rN  ry  r  r!   r   r0  r  ry  r  rG  r   )
r  rl  r_  
claude_binr   r"  outerinnerarr_mr   s
             r2   _parse_prd_clauder  	  s   	& - !	!  /J%v'8&A
 !LL;FMM<O<O<Q;RSTImm!!#	JJsOE%&%&8u+<heS)::e,,eT* L 		)S"))4::ekk!n--^_	 ## 		 $$ =>	 >qcBC	sa   AE7 *E7 &E ,=E *E <A
E7 E7 E41E7 3E44E7 7*G#G+G
G
Gc                 j   t        |       }|j                         s	t        | z  }|j                         sdd|  dS |j                  d      }t	        |j                               }t        |      }d}|s#t        j                  d       t        ||      }d}|j                  }|j                  d	      r|d
d n|}t        dz  dz  }	|	j                  dd       g }
g }t        |      D ]  \  }}|j                  d|      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  dd      }|j                  d      xs g }|j                  d      }d| d| }|	| dz  }|j                         r|j                  t	        |             g }|dkD  r$||dz
     j                  d|dz
        }d| d| g}t!        j"                         j%                  d      }|rd j'                  |      nd}d!| d!}|r|d"| z  }t)        |      }d}|rd#| d$}d%| d&| d'| d(| d)| d*| d*| d+| d,| d-| d$| }|j+                  |d       |
j                  t	        |             t        j                  d.|         d/| |||
|t-        |      d0S )1u  PRD 파일을 읽어 Phase별 task 파일을 생성한다. 위임은 하지 않는다.

    Args:
        prd_path: PRD 파일 경로 (절대경로 또는 WORKSPACE 기준 상대경로)
        team_id:  담당 팀 ID

    Returns:
        처리 결과 딕셔너리
    r   u'   PRD 파일을 찾을 수 없습니다: r/  r   r   regexuE   [handle_prd] 정규식 파싱 결과 없음, claude CLI 폴백 시도claudezprd-r  NrJ   r   Tr   rn  ro  Phaserp  r   rq  rr  rs  rt  z	dispatch-z-phaser  r   r  r  )timespecr   `u    — u   
## 완료 조건 (DoD)
r[  z---
task_id: "z	"
team: "z$"
level: 2
priority: P2
depends_on: z
created_at: "z"
deadline: null
---

# rc  r  u   

## PRD 참조 (필수)


---

u   [handle_prd] 파일 생성: r5  )r   prdr   methodcreatedskippedtotal_phases)r   r   r   r  r!   resolver  r   r   r  stemr  r   rw  r   r   r   r  r  rP  reprr  r  )r  r   prd_filerl  prd_absrV  r  r  prd_stemr@  r  r  idxr_  	phase_numro  rp  rq  rr  rs  rt  r   	file_path
depends_onprev_phase_num
created_atfeatures_strprd_ref_linedepends_on_yamldod_sectionr~   s                                  r2   
handle_prdr  5
  s,    H~H??x'??!0WX`Wa.bcc$$g$6K(""$%G k*FF[\"7K8 ==D??62tABxHH$w.IOOD4O0GG' 6@
UIInc2	YY|W5
		'2&99Z,yy$#ii
39riihZvi[97)3/	NN3y>* !#
7#C!G_00qIN%hZvn5EFGJ\\^--y-A
.6tyy*B7)1~eL>22Lz*6se2>K 	 "Y  ++ ,&< ( 
!J<q2eW ='n fBm 	$ 	Ww7s9~&29+>?m6@t F r?   
session_idrefresh_maprV  resume_from
agent_typec                 %   |=t        |      }|j                         sdd| dS |j                  d      }d| d| }| s|sdd	dS | r|rdd
dS |r2t        |d   |       t	        |	xs d|||       t        ||||	||      S | J t        | ||      }|rd|dS d}|	t               }	|	}nt        |	       |t        ||	|       t        rt        	 t        |      }|j                  s|j                  D cg c]  }|j                  dv s| }}|r1t        j!                  d|D cg c]  }|j"                   c}        n:t        j%                  d|j                  D cg c]  }|j"                   c}        t*        rNt,        H	 | d| }t-        |      }t        j%                  d|j.                   d|  d| d|j0                          |}|4t2        r.t4        (	 t5        |      }t        j%                  d| d|         n|rt        j%                  d| d|         t        j%                  d|  d|	        t6        rt8        	 t9        t;        t<                    }|j?                         }|jA                         } |jC                         D ]o  \  }!}"|"jE                  d      | k7  r| jE                  |!i       }#|jG                  |!|"|#      }$|$d   d k(  sIt        j!                  d!|!|$jE                  d"d#             q 	 dd%l$m%}%  |%       }&|&jM                  d&      r|}'nd'}'d}(d})|
0|	r.tO        jP                  d(|	      }*|*r|*jS                  d)      })d*|) }(|xs | tT        v }+t<        d+z  d,z  },|,j                         r	 tW        |,d-d      5 }-tY        jZ                  |-      }.ddd       .jE                  d.i       jC                         D ]  \  }/}0|0jE                  d/      d0k(  s|0jE                  d      | k(  s0|/|	k7  s6|+s/t]        |	       dd1|  d2|/ d3|0jE                  d4d5      dd6  d7dc S t        j!                  d8|  d9|/ d3|0jE                  d4d5      dd6  d:| tT        v rd;nd< d=	        n |dd? tc        |      d?kD  rd@nd5z   }2|	}3dAt;        td              dB|3dC| dD|2g}4|r|4jg                  dE|g       |r|4jg                  dF|g       ti        jj                  |4dGdGd6H      }5|5jl                  dk7  r3t        j!                  dI|3 dJ|5jn                  jq                                 |/t<        dKz  |z  }6|6j                         st]        |3       ddL|6 dS |r|rt<        d+z  dMz  | dNz  }7dO}8|7j                         sdG}8nI|7js                         jt                  }9tw        jx                         j{                         |9z
  dPz  }:|:dQkD  rdG}8|8rqt<        dKz  |z  }6|6j                         rUt<        dRz  dSz  };|;j                         r9ti        jj                  dAt;        |;      t;        |6      dTt;        |7      gdGdGd6H       tO        j|                  dUdV|	 dW|d)X      }t        | |       t	        |	|||       t        ||       t        |       t        |      }<|<rt        j!                  dY|<        t        |t;        t<                    }|dZk(  rt        |t;        t<                    }t<        d+z  d.z  |	 dNz  }=|=j                  j                  dGdG[       |=j                  |d       t        rt        	 t        |	t;        |=      d\d']       t        |       }>|>rt        j!                  d_|>        t        | ||	||||`      }?dadbdcdd}@|@jE                  |da      }At        r/t        )t        A      r|?dez  }?t        j%                  dfA dg       t        |      }Bt        |Bt;        t<                    }Bt        |      }C|BrCt        B|	      }D|DD ]  }Et        j!                  dh|E         t        D       t        |3Bi       t        |C      }F|Frt        j!                  dhF        t        |B      \  }G}Hdadbdcdd}@|@jE                  |da      }I|I|Gk  r$Hr"t        j!                  djI dk| dlH dmG dn	       |rt        |3|o       |<r	|?dp|< dqz  }?Br.t        B      }J|Jr!drj                  ds JD              }K|?dt|K duz  }?t        |      }L|Lr|?dvL z  }?| dwk(  r9t        r3t        -	 t        |      }M|?dx|M dyz  }?t        j%                  dz|M        t        |    }N| tT        v r|	 t        |3||      }Ot        O   }Pt        jE                  |P      }Q|QHt        j!                  d}Pj                          d~       t]        |3       dd|Pj                          dS | }RO}Snt        jE                  |       }T|TrTt        vrt]        |3       dd|  ddS t        T   }Q|QHt        j!                  d}Tj                          d~       t]        |3       dd|Tj                          dS | j                  dd5      }Rt        jE                  | d5      }S|Srt        |3S       Srt        |	      }Ut        j%                  dS d|  dt        |Uj                                       |S|Uv rUS   }V|Vd   }W|Vd   }X|W|	k7  rX| k7  r|sHt]        |3       t        U      }Ydj                  d |YD              }ZddS dX dW d|YrdZ ndz   YdS t        j!                  dS dX dW d       nt        j%                  dS d       Srt        |3RS|xs dd|	 dN       dadbdcdd}[|[jE                  |da      }\|\dbk\  r&t        |	\      }]|]rt        j%                  d]        t        |	|||      }^|^r	|?d^ drz  }?t	        |	|||       dO}_d}`Q}a|dv r3ar1t        ad      }`|`r#`dk7  rdG}_t        j%                  d| d` d       d}bdd|?dt        |b      dt        dQdg
}c	 ti        jj                  cdGdGd?H      }ddjl                  dk(  r	 tY        j                  dj                        }eddlpmq}f  |f| d       t        |       }gt        j%                  d|  d|gd    d|gd   xs d dgd   xs d        t                _r`rt        a`d6       |dd? tc        |      d?kD  rd@nd5z   }2t        r	 t        |2      }2d|	 dNd    dk|  d|2 }hti        jj                  dAt;        td              d|hdd\gdGdGd6H       |rt        || |	       |
|(|)g }it        d)|
d)z         D ]&  }jd|) d|j }kd|k dN}lij                  |j|l| |kd       ( dAt;        t<        dz        dd|(dtY        j                  idO      d|=g	}mti        jj                  |mdGdGd6H      }n|njl                  dk7  r4t        j!                  d|( dJnjn                  jq                                 nt        j%                  d|(        Nd   }ot        r	 t        o      }ot        j%                  d|	 do        d|	| Nd   |||Nd    dȝedɜ}p|(|(pd<   t        et              rejE                  d˫      nd}q|qrqddad̜}r|=r|=rd<   t        |	fi r 	 ddl}m~}s  |s|	|      }t|tr>t        j%                  dtjE                  dЫ       d|	        |tjE                  dЫ      pd<   pS t        j                  ddjn                  jq                                 _r&`r$t        a`       t        j%                  d|a        t]        |3       ddjn                  jq                         dj                  cddc dgz         d֜S c c}w c c}w c c}w # t&        $ r#}t        j)                  d|        Y d}~d}~ww xY w# t&        $ r#}t        j)                  d|        Y d}~d}~ww xY w# t&        $ r#}t        j)                  d|        Y d}~bd}~ww xY w# t&        $ r!}t        j)                  d$|       Y d}~d}~ww xY w# 1 sw Y   xY w# tX        j^                  t`        f$ r#}1t        j!                  d>|1        Y d}1~1d}1~1ww xY w# t&        $ r#}t        j)                  d^|        Y d}~	d}~ww xY w# t&        $ r#}1t        j!                  d{|1        Y d}1~1d}1~1ww xY w# t        $ r#}1t]        |3       dt;        |1      dcY d}1~1S d}1~1ww xY w# th        j                  $ r( t        j                  d       t]        |3       dddcY S w xY w# tX        j^                  $ r  ddj                  jq                         i}eY Hw xY w# t&        $ r#}t        j)                  d|        Y d}~d}~ww xY w# t&        $ r	 Nd   }oY w xY w# t&        $ r#}ut        j!                  du        Y d}u~upS d}u~uww xY w)u  작업을 독립 세션으로 디스패치

    목차→요약→상세 원칙:
    - task_desc를 memory/tasks/<task_id>.md에 저장 (요약 파일)
    - 프롬프트 본문에는 파일 경로만 포함 (build_prompt 내에서 처리)
    - session_id: 아누의 현재 세션 ID. 전달 시 followup이 동일 대화에서 실행됨.
    - task_type: 작업 유형 (coding/research/check). coding일 때만 QC 검증 포함.
    Nr   1   resume_from 파일이 존재하지 않습니다: r/  r   r   uL   ## 이전 세션 요약
아래 요약을 읽고 이어서 작업하세요.

r  u0   team_id 또는 composite_teams 중 하나 필수u2   team_id와 composite_teams는 동시 사용 불가r   r   )r   r-  r   r  uh   [injection_guard] task_desc에서 위험 패턴 감지 (차단 없음, 워크플로우 호환): threats=uG   [injection_guard] task_desc에서 낮은 위험 패턴 감지: threats=r  rc  r  rY  z, task_type=r  r  r  r  r  u   위임 시작: team=rZ  r   r   r  r  u>   [session-health] CRITICAL 세션 감지: task=%s, usage=%.1f%%	usage_pctg        u2   [session-health] 세션 체크 실패 (무시): %s)FeatureFlagLoaderrw_isolation_enabledr  ztask-(\d+)\.r  zscoped-rJ   r   r   r   r   r   u   같은 팀(r  z ('r  r   r=  uN   '). 완료 후 재시도하거나 --force 플래그로 강제 실행하세요.u   [force] 같은 팀(r  z'). u   논리적 팀 자동 --forceu   --force 플래그로u    강제 진행.r  r   r  r   r  r  r  	--projectz--work-levelTr>  r  r   rN   u8   프로젝트 디렉토리가 존재하지 않습니다: zproject-mapsr  Fi     r:  zproject-map.pyz--outputr	  r
  r  r  z[file-size-check] codingr   r  r  r  z	[qc-env] rN  r   r	  r  r  r  r  u+    작업에 sanitize 게이트 지시 삽입z[affected_files] )r8  u   [level-estimate] ⚠️ Lv.(u   ) 지정했지만 u    — Lv.u    이상 권장)r  u(   

## ⚠️ 참조 파일 크기 주의
u   
- 위 파일은 반드시 offset/limit 파라미터를 사용하여 분할 읽기하세요.
- 요약 파일(*.summary.md)이 있으면 원본 대신 요약 파일을 먼저 읽으세요.
- 한 번에 200줄 이상 읽지 마세요.r[  c              3   :   K   | ]  }d |d    d|d    d  yw)z  - r   rk  r1  u   줄)Nr=   )r  lfs     r2   r  zdispatch.<locals>.<genexpr>  s'      _B46
|2bk]$!G _s   u@   

## ⚠️ 대형 파일 프로토콜 (2000줄 초과)
대상:
u  
1. 전체 읽기 금지 → offset/limit으로 수정 대상 ±200줄만 읽기
2. Edit 전 수정 위치의 정확한 라인 번호 확인
3. Edit 후 반드시 grep -n으로 변경 반영 확인
4. 한 번에 50줄 이상 삽입 금지 → 여러 Edit으로 분할
z

r|   u?   

## 🎨 이미지 스킬 라우터 추천
- 추천 스킬: **u   **
- 이 추천은 image-skill-router.py의 `get_skill_recommendation()` 결과입니다.
- 추천 스킬을 우선 사용하되, 작업 특성상 다른 스킬이 적합하면 사유를 명시하세요.
u/   [image-skill-router] design 팀 추천 스킬: u-   [image-skill-router] 추천 실패 (무시): r  r  r  r  u   팀 uH   에 할당된 봇이 없습니다. 봇 토큰을 먼저 등록하세요.r  )r   r   u   [봇 충돌 검사] bot=z, busy_bots=r   r   c              3   8   K   | ]  }|d     d|d    d  yw)r   r  r   r  Nr=   )r  as     r2   r  zdispatch.<locals>.<genexpr>D  s)     1k\]Qx[M1^CTBUUV2W1ks   u   봇 u   가 u    작업(u   )에 점유 중입니다. u   가용 대안: u"   모든 봇이 작업 중입니다.)r   rf  available_botsu   [force] 봇 u2   )에 점유 중이지만 --force로 강제 진행.u    [봇 충돌 검사] 통과: bot=u    가용defaultr,  )rx   r   r   r;  u&   [3docs] 작업 3문서 생성 완료: u    

## ⚠️ Agent 미팅 경고
)r  r  claude-opus-4-6z[opus-upgrade] level=u   , 봇 모델 Opus 승격: u    → claude-opus-4-6r]  r  r  r  r  r  r   r!  r"  r#  r%  u   [모델 검증 결과] z: consistent=r   z, org=r   r   r   r   )r   u$   [redact] 마스킹 실패 (무시): r&  z] leaderr'  r(  r)  r  .)orderr;  r   r   zchain_manager.pycreatez
--chain-idz--tasksr   z--original-task-fileu)   chain_manager.py create 실패 (chain_id=u)   chain_manager.py create 완료: chain_id=u   위임 완료: r   r*  u?   에게 위임 완료. 즉시 독립 세션에서 작업 시작.)r   r   r   r   r  r  rf  r+  rK  rx  )r-  r7  r.  r;  r/  r1  r2  r3  r4  u   위임 실패: u<   [opus-upgrade] 디스패치 실패로 모델 즉시 복원: r5  r6  )r   r   r  r  r  rk  r  r'  r+  ri  r8  r9  r:  r;  r<  r   r   r=  r   r   r   r>  r?  r@  rA  rB  rC  r  r  r!   r   _load_running_tasks_load_ledger_tasksr   r   check_sessionutils.feature_flagsr  
is_enabledr0  r  r  r  r   r$   r   rd  r   r,   r  rD  r  r   rD  rE  r   r/  statst_mtimer   r  	timestampr   r  rf  rz  r  r  r   r   r  rE  rF  r  r5   rH  rI  r7  rM  r  rX  rj  r(  rn  r  r  rP  r  _IMAGE_SKILL_ROUTER_AVAILABLE_get_skill_recommendationr4   r   r   rJ  r   rK  r   r)   r   r   ry  r   r   r  r  r   r  r   rG  r   rF  r   rL  r$  r   r$  r   rM  r>   ra  r  r   r`  rN  r  rO  r0  )vr   r)  r  r  r  rJ  rK  r  rL  r   rV  r-  r  r  r   r  r  r  r  resume_pathsummary_contentrouting_warningrg  rP  r  rQ  rR  rS  rT  rU  _sr_running_ledger_rtid_rtinfo_linfo_statusr  	_rw_flagseffective_agent_type_phases_chain_id_phases_base_num_base_matcheffective_forcer   r1   rY  rZ  r[  r   rV  r   rW  rX  project_dirmap_pathneeds_refreshmtime	age_hoursscriptfile_size_warningsr;  qc_env_warningr_  _level_to_int_dispatch_level_af_task_lv_overlap_warnings_ow_af_missing_warn
_est_level_est_reason_manual_level_large_files_lf_listplatform_rulesrecommended_skillr   r   r   rL  rx   bot_id_metar   busy_bots_infoconflictconflict_task_idconflict_team_idr   available_str_level_to_int_docs_docs_level
_docs_path_meeting_warning_opus_upgraded_opus_original_model_opus_key_hashrb  rI  r   rc  r$  consistencyre  _tasks_json_i_t_id
_task_file_chain_create_cmd_chain_result_log_leaderrf  rg  rh  r0  ri  rj  sv                                                                                                                         r2   r  r  
  s   > ;'!!#!N{m\  &///AE  k	 	 ?!.`aa?!.bcc %oa&8)D!'"6Y	5J\]"?Iug]bjopp  -WiAQRO!o>>L"$ 	 ( #IwE "m&?	L(3L'',8,@,@ gqAJJRfDf g g NN#<I#JqANN#J"KM
 KK#<H<P<P#QqANN#Q"RT ~9		E!*1YK8O-o>KK()9)D)D(E FyYK 8,==>@ &+!&=,BZ	L!-i!8KK89K8LGT[S\]^ 
>?Q>RRYZaYbcd
KK&wiz'CD %);)G	S$C	NCC..0H,,.G"*.."2 
w;;y)W4 UB/++E7FC7#z1NNXK5
 6!#I23)& '+&*gii9*003!()9(:; =+< <O X%(::J	Dj#8 -A $		!-/</@/@"/M/S/S/U +m!%%h/9<%)))4?$/*%g.&-"-gY6e#/.M4E4EmUW4XY\Z\4]3^ _m!n   NN-gY6\'.M,=,=mR,PQTRT,U+VVZ=DHY=Y9_uv  wFG
 +4 3BC	NR,?5RHJMC
OWmXwX`blmI+z23.%01>>)DtUWXL!#:=/\M`M`MfMfMhLijk *,z9!!#-(%4lmxly2z{{ kx'.8j\;MM  M MMO,,E!113e;tCI2~ $#j0:=K!!#"Y.1AA==?NN"CK[1A:sS[}]'+! "	 +r'!_iqQI "'95gy%9KL Iy1)$5i@+,>+?@A ,Is9~FIH29c)nM	 H$w.G9C@I4$7W5 #6#B	PY!	 (0N>"234GUzH`iF
  !aQ?M#''q1O$4$@EUVeEfK	
 	)/)::efg  	
*C
)#s9~
>C +H
9#wG$ 	6CNN.se45	6&'89mC@3IxH*+;*<=>29cBJ aQ?M!%%eQ/Mz!k)-%@RS^R__klvkw  xF  G	
 mh?9:L9M=>	
 ',yy _R^ __H$: &Z[F ,I6ND()) (<AZAf
	P 9) D&&7%8 9FGF KKIJ[I\]^ WD ##	:2=QVWL l+ll8$;NN78H7ITU-(%4jkskykyk{j|2}~~" g&x/-(%D	  BJ  3K  L  Lv;NN77GzRS-(%4jkqkwkwkyjz2{||w+$(("5!-[A0INKK*;-wwiGWX\]k]p]p]rXsWtu n,)+6#+I#6 #+I#6 #w.3Cw3N %m4$B>$R	(,		1kaj1k(k&-"&{m48H7IRbQc d8 !9HQ_]O#DW{!} /8   NN&{m48H7IRbQc dH I
 >{m7ST $1	%gYc2	
 %&1!D$((2Ka&w<
KK@MN ,GY|T78H7ILL gy%9KL NN((^-n>OP$8<M$M!NKK'w.HMaLbbvw O 	/*C]DtRP A	6zz&--0H
 	6w-1':%gYmK<U;V W{+4u5VK<T<]X];^`	
 	 2#N4HPRS
 s^I0CuL
J)*5
 gYbh 0':J:,WJ:N		
 x': "2">CSC_ "KAvz* 
 012$7,UG37
""!#%/ '#(	
 I 223 

;U;&
! 'NN+<TX\fhiM''1,?@P?QQTYfYmYmYsYsYuXvw GHXGYZ[8n-*;7 	ogYeK=AB"N$x.))hi%	
 '"2GJ .8$-Gx||D)T+ N
 .7{+!'<^<	S3 )4I8w9O8PPUV]U^_`#,==#9  v}}':':'<&=>?2>+?@KKVWeVfghm$!fmm.A.A.CPSPXPXY\]_^_Y`dqcrYrPstt{ !h $K
 $R 	LLLEbTJKK	L  	ELL>rdCDD	E  	LLLEbTJKK	L0  	SLLMrRR	S<- -0 $$g. 	DNN>qcBCC	Db  	PLLI"NOO	Px  	PNNJ1#NOO	P  	:-(%#a&99	:f $$ ]BDm$!.[\\] ## 	6v}}2245H	62  JCB4HIIJf  -"8n-H  	SNNGyQRR	Ss  %A@ ;99A@ +>
>(A@ &A@
9	A@ AA@7 '&AA& BAB %)AB AC AC5AAC 8AC AC 0AC AAC AD ,AD= AE, :AF $AG 
AH AH> AAI 9A@ @	A@4@A@/@/A@4@7	AA#A AAAAA#A&	ABA/ABBABB	AB?BAB:B:AB?CACCAC CADC(ADDADD	AD:DAD5D5AD:D=	AE)EAE$E$AE)E,	AFE5AFFAFFAFF8AGGAGG/AHHAHH	AH;HAH6H6AH;H>AIIAII	AI?IAI:I:AI?c            	         ddl m}   |         t        j                  d      }|j	                  dddd	       |j                  d
      }|j	                  dg dd       |j	                  dd       |j                  d
      }|j	                  dd       |j	                  dd       |j	                  ddg dd       |j	                  ddg dd       |j	                  dd d !       |j	                  d"d d#!       |j	                  d$d d%!       |j	                  d&t        j                  d'd(	       |j	                  d)d d*!       |j	                  d+d d,!       |j	                  d-t        d d./       |j	                  d0ddd1	       |j	                  d2d d3d45       |j	                  d6d7gd d89       |j	                  d:ddd;	       |j	                  d<ddd=	       |j	                  d>d?d@gd@dA9       |j	                  dBd dC!       |j	                  dDdddEdFG       |j	                  dHd dIdJ5       |j	                  dKdddLdMG       |j	                  dNd dO!       |j                         }|j                  r,t               }t        t        j                  |ddPQ             y |j                  s|j                  s|j                  dR       t!        |dSd       r_|j                  s|j                  dT       t#        |j$                  |j                        }t        t        j                  |ddPQ             y |j&                  s6|j(                  s*t!        |dSd       s|j*                  s|j                  dU       dV}|j&                  r|j&                  }n2|j(                  r&	 t-        |j(                        j/                  dWX      }dY|v sdZ|v r8t2        j5                  d[       t        t        j                  d\d]d^d_             |j(                  rt-        |j(                        }|j7                         sEt        t        j                  d`da|j(                   d^d_             t9        j:                  db       |j/                  dWX      j=                         st        t        j                  d`dc|j(                   d^d_             t9        j:                  db       n@|j&                  xs dVt?              ddkD  r"t2        j5                  det?               df       |j@                  d7k(  rM	 ddgl!m"}  ||jF                  xs dhi      }	|	 dj |jH                  sdk|_$        t2        jK                  dl       |j@                  eg dn}tO        fdo|D              rMt!        |dpd      rt2        j5                  dq       n*t2        j                  dr       t9        j:                  db       |jP                  jt-        |jP                        }|j7                         sEt        t        j                  d`ds|jP                   d^d_             t9        j:                  db       d }|j                  r	 tS        |j                        }|j*                  r |jF                  r8t        t        j                  d`dtd^d_             t9        j:                  db       |j                  s8t        t        j                  d`dud^d_             t9        j:                  db       |j&                  s|j(                  stY        jZ                  dvdV|j*                        }t-        t\              dwz  dxz  | dyz  }|j7                         r!|j/                  dWX      j=                         n;t        t        j                  d`dz| d^d_             t9        j:                  db       t_        |j*                  |j                  d{ta               v rndV|jb                  |      }|d}   d`k(  r5t        t        j                  |d_             t9        j:                  db       |d~   |_#        t2        jK                  d|j*                   d|jF                   d|d    d       |jF                  r[tY        jd                  d      }|jg                  |jF                        s+t2        j5                  d|jF                  |jh                         tk        di d|j                  d{d|jl                  d|d|jn                  d|jp                  d|jr                  d|jt                  d|jv                  d|jF                  d|jx                  d|jb                  d3|jP                  d|jz                  d|jH                  dE|j|                  dI|j~                  d|j                  dL|j                  }t        t        j                  |ddPQ             y # t0        $ r Y Sw xY w# tL        $ rF}
t        t        j                  d`dm|
 d^d_             t9        j:                  db       Y d }
~
d }
~
ww xY w# tT        $ rL}
t        t        j                  d`tW        |
      d^d_             t9        j:                  db       Y d }
~
4d }
~
ww xY w)Nr   r	   u   작업 위임 디스패처)r  z--check-sessions
store_trueFu0   모든 running 세션의 토큰 사용량 체크)actionr  help)requiredr  )rf   rg   rh   ri   rj   rk   rl   rm   r   r   r   r|   r~   u   위임할 팀)choicesr  z--compositeuE   쉼표 구분 논리적 팀 ID 목록 (2~3개, 예: marketing,design))r  z--tasku%   작업 설명 (짧은 한 줄 용도)z--task-fileuG   작업 설명 파일 경로 (권장: 긴 내용은 반드시 파일로)z--levelr  r  u(   검증 레벨 (normal/critical/security))r  r  r  r)  r  )r  r  checku[   작업 유형 - coding: QC 검증 포함, research/check: QC 검증 제외 (기본: coding)z	--sessionu9   아누 세션 ID (followup을 현재 대화에서 실행))r  r  r  u2   프로젝트 ID (projects/ 하위 디렉토리명)z--chainu   체인 ID (chain.py 연동)z--refresh-mapTuQ   프로젝트 맵 자동 갱신 (24시간 이상 오래된 경우, 기본: 자동)z	--task-idu8   태스크 ID 직접 지정 (미지정 시 자동 생성)z--resumeu[   재시도할 base task ID (예: task-2133). 자동 채번 후 기존 task 파일 재활용.z--phasesuf   한정승인(scoped delegation) Phase 수. 지정 시 chain_manager.py create로 체인 자동 생성.)rz  r  r  z--forceuW   동일 팀에 running 태스크가 있어도 강제로 dispatch 허용 (기본: 거부)z--resume-fromr  uj   이전 세션 요약 파일 경로. 지정 시 task_desc 앞에 요약을 prepend하여 새 세션 시작.)r  destr  z
--workflowzimage-qc-gateuS   워크플로우 적용 (image-qc-gate: 이미지 QC 게이트 5Phase 자동 적용))r  r  r  z--skip-qc-gateua   이미지/광고 작업의 QC 게이트를 의도적으로 스킵 (제이회장님 승인 필수)z--skip-meetinguH   Lv.4 Agent 미팅 체크를 의도적으로 스킵 (로그에 기록됨)z--agent-typereadr  uo   에이전트 유형: read(읽기 전용, worktree 미생성) | write(쓰기, worktree 생성). 기본값: writez--modeluW   모델 강제 지정 (예: claude-opus-4-6). 지정 시 해당 모델의 봇만 선택.z--override-routingr  u?   라우팅 경고를 무시하고 지정 팀으로 강제 위임)r
  r  r  r  z
--batch-idr  uY   배치 ID. 병렬 위임 시 동일 batch_id를 부여하여 전팀 완료 추적 가능.z--skip-brainstormingr  u?   Lv.3+ UX 작업의 brainstorming 사전 실행 체크를 스킵z--prduW   PRD 파일 경로. Phase별 task 파일 자동 생성 (위임 없음, 파일 생성만)r   r   uK   --check-sessions이 없으면 --team 또는 --composite이 필수입니다.r  u+   --prd 사용 시 --team이 필수입니다.u]   --check-sessions이 없으면 --task, --task-file, --prd, 또는 --resume이 필수입니다.r   r   r   u   레벨: Lv.0u   ## 레벨: Lv.0um   ⚠️ Lv.0 마이크로 수정은 dispatch 불필요! 아누가 Task tool (haiku)로 직접 실행하세요.r   uF   Lv.0은 dispatch 불필요. Task tool (haiku)로 직접 실행 권장.r/  r  r   u3   작업 설명 파일이 존재하지 않습니다: r  u,   작업 설명 파일이 비어있습니다: d   u   --task 직접 전달 u;   자 — 긴 내용은 --task-file 사용을 권장합니다)build_workflow_overview_promptauto)r   r  r  u3   [workflow] image-qc-gate: opus 모델 강제 적용u&   워크플로우 모듈 import 실패: )r  u   광고r  r  r  r  c              3   &   K   | ]  }|v  
 y wr<   r=   r  s     r2   r  zmain.<locals>.<genexpr>  s     92rY9r  skip_qc_gateuV   ⚠️ --skip-qc-gate로 이미지 QC 게이트 우회. 제이회장님 승인 필수.u   ❌ 이미지/광고 작업에 --workflow image-qc-gate가 필수입니다. 의도적 스킵 시 --skip-qc-gate 플래그를 추가하세요.r  u-   --resume과 --task-id는 동시 사용 불가u.   --resume 사용 시 --team이 필수입니다.r1  rJ   r   r  u"   base task 파일이 없습니다: r)  )r-  r   r6  z	[resume] r   r4  r7  r  z'^task-\d+(_\d+\.\d+)?(_[a-z])?(\+\d+)?$uV   [task-id-format] --task-id '%s'가 포맷 v2 규칙에 맞지 않습니다. 패턴: %sr   r  r  r  rJ  rK  r  rL  r   rV  r-  r  r   r  r=   )Butils.env_loaderr
   argparseArgumentParseradd_argumentadd_mutually_exclusive_groupBooleanOptionalActionr  
parse_argsr  rO  r$   r`  r   r  r   getattrr  r  r`  r;  resumer   r  r   r   r   r   rP  rQ  r/  r  workflowprompts.image_workflowr  r   r   r   ImportErrorr  r  r  r  r!   r0  r   r   rI  r   r-  r  r  rr  r  r  sessionprojectchainr  rz  rV  r  r  r  r  r  )r
   parserteam_or_composite
task_groupargsr   task_contenttask_file_pathr  workflow_promptr   _image_keywordsr  composite_teams_list
base_cleanbase_task_fileresolve_resultTASK_ID_V2_PATTERNr)  s                     @r2   mainr2    s{
   .O$$1MNF ?	   ;;U;K""
 # # & ""T #  44e4DJH+RSM0yz
27	   /j	   T8st
T8lm
	46ST
--`	   T8rs

D  8U  V
u	   f	   y	    !b	   p	   W	   !~	   f  
 N   h	   !N   f   D !djjeA>? 99T^^bc tUD!yyLLFGDHHdii0djjeA>? 99T^^GD%4NW[WbWbtu Lyyyy		/9979KL %):l)J  G  	HJJ$1yz"	
 ~~dnn-$$&

&5himiwiwhx3yz!& HHQK",,g,>DDF	

&5abfbpbpaq3rs!& HHQKIIO	y>CNN23y>2BB}~ }}'	M<.O ++;ykBI::.
QR }}[999t^U3wxX  #4++,!!#

")%VW[WgWgVh#i "' HHQK  ~~	#<T^^#L  {{<<$**<kl  |A  B  CHHQKyy$**<lm  }B  C  DHHQKyy	2t{{;J!)_x7'AzlRUDVVN$$&*44g4FLLN	djjGBdesdt@u!v  FK  L  M(diik]`]bNbhjrvr|r|}(#w.$**^%@AHHQK%m4i}E$,,yXeIfHgghij||ZZ(RS!''5NNh"**  		 jj -	
 << <<  $$ ))  {{ jj $$ ?? jj  ..!" #$ &&%&  22'F* 
$**V%
:;w  		d  	

&5[\][^3_`!& HHQKK	T  	$**CFCRWXYHHQKK	sD   ,%h ?Ah( i: 	h%$h%(	i71;i22i7:	kAk

k__main__r<   )r=  )r   N)F)r]  )r  NNr  )i  )r  NFN)Nr   r  NNNNTr  NNFNr  NFNFF)__doc__r  r   r$   r"   r0  r   rP  r   r   pathlibr   typingr   r   r   r   rq  r!   __file__r   r  r
   r   utils.atomic_writer   config.loaderr3   get_instancer  r  rG  r4   r5   rR  utils.loggerr6   r!  utils.composite_constantsr7   r8   __name__r   utils.redactr9   r>   rM  utils.injection_guardr@   r9  r8  utils.approvalrA   r?  r>  utils.audit_loggerrB   rF  rE  utils.model_routerrC   rC  rB  utils.sanitize_gaterD   rI  rH  utils.bot_statusrE   r   r   utils.session_resiliencerF   r  r  importlib.utilutil_iluspec_from_file_location	_isr_specmodule_from_spec_isr_modloaderexec_moduleget_skill_recommendationr  r  get_path_ws_fallbackr^  r   r   r   rD  r  _chat_fallbackr   rp  r   r  r   utils.org_loaderra   rb   rc   r   rJ  r   
_teams_map_team_to_bot_mapr  r   CROSS_FUNCTIONALr  r   r   r   ry  r   r   r   r   r  r   r   r$  r(  r7  rM  rX  rj  rn  r  r  r  r  r  r  r  tupler  r  r  r  r  r  r
  r  r  r'  r+  rI  ra  rd  rf  ri  rz  r  r  r  r  r  r  r  r  r  r  rk  r  r  r  r  r2  )r   r   kvs   0000r2   <module>rZ     sV      	 	  
 (  & &	HHOOAs4>0012.O
42+%=%%'D(.G'
 
H		B'C!%
 >
$L"
$>"
%G#
"F 
*P$(!
*!,,,DN!!G+.EEFI %t$$Y/H  * ( A A$(! 4Et}}./Sh 0,?@	x"??!O3
 2Ct""9-Q]
**..+^
<$  DIIK)+j8;bbemm  ::>>,-JJNN./JJNN./JJNN./JJNN./JJNN./JJNN./JJNN./JJNN./
-
cc!#H%'J-/NX '
?'1CD*4IJ*H=	  S  )7(<(<(>?1q!t? 't 'tCcSVhDW?X 'T)`d
 )`c )`Xd3S#X3F.G DQUVY[^V^Q_L` 0 "&ff$Jf 	fR %, c S#X 0 >E !S ! !s !t !Hec e# ec e4 e*B B BJf`R3 C D F+S +T +\<D <# <RV <~!$ ! !QU !HJT Jd J*C S ( [ *3 * * *Zc 3 3 6<$ <4 <~'ZS 'ZT 'ZV C  % D  6# 6# 6c 6QU 6bjknbo 6tBs B3 B3C BJ >S T 8+S +# +#T #c #LD# DNS T :SX# SX SX SXD SX]a SXt  $"  	
  sm  	$! !s !S !T !H23 24 2j
S 
T 

3 
 
C 
TX 
"(5C (5HSM (5V" " "JT c T .Bc Bhsm BJ)
s )
s )
t )
XM
3 M
3 M
4 M
`#s #s #3 #\` #mq #LT @#1S #1Xc] #1L'C 'C '4 '\deh\i 'T c 8 !Gu#YGuGu Gu c]	Gu
 Gu C=Gu 
Gu^9# 9$t* 9x; ;# ;$t* ;|g gs gt gV "+/ $ $"! !%""$'r	uc]r	ur	u r	u d3i(	r	u
 r	u r	u smr	u r	u r	u c]r	u SMr	u r	u #r	u r	u C=r	u  !r	u" sm#r	u$ %r	u& 'r	u( 
)r	ujH<V
 zF q  		   3 s t :  D  (.G''("  3 3    'M!&'   N   $#$  $L#$  %$%  "!"  *$)!*  * $$)!*P  '
T&&w/
,,];
#./CSCYCYC[\<4fj..\\
  	
 	

 !       	
='
j @s  ?X" 8X. ?Y Y <Y4 Z
 Z Z*  Z: )[
 2[ ;[* A0[: =\
 ^"X+*X+.YY	YYY10Y14ZZ
	ZZ	Z'&Z'*	Z76Z7:	[[
	[[	['&['*	[76[7:	\\
A^])(-^^