+
    ik?                       a  R% t#0 t R t^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIH	t	 ^ RI
H
t
 ^ RIHtHtHtHtHt ]	! ]4      P$                  R,          R,          R,          tR^R	^R
^</t/ t] ^ k R R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR&R R  llt R'R! R" llt!R# R$ lt"R# )(zBird X search client - vendored Twitter GraphQL search for /last30days v2.1.

Uses a vendored subset of @steipete/bird v0.8.0 (MIT License) to search X
via Twitter's GraphQL API. No external `bird` CLI binary needed - just Node.js 22+.
N)Path)datetime)AnyDictListOptionalTuplevendorzbird-searchzbird-search.mjsquickdefaultdeepc                \    V ^8  d   QhR\         \        ,          R\         \        ,          /# )   
auth_tokenct0r   str)formats   "R/Users/bowang/.openclaw/workspace/skills/last30days-official/scripts/lib/bird_x.py__annotate__r      s"     " " "HSM "    c                N    V '       d
   V \         R&   V'       d   V\         R&   R# R# )zIInject AUTH_TOKEN/CT0 from .env config so Node subprocesses can use them.
AUTH_TOKENCT0N)_credentials)r   r   s   &&r   set_credentialsr      s!    %/\"
!U r   c                F    V ^8  d   QhR\         \        \        3,          /# r   return)r   r   )r   s   "r   r   r   '   s      c3h r   c                 l    \         P                  P                  4       p V P                  \        4       V # )zCBuild env dict for Node subprocesses, merging injected credentials.)osenvironcopyupdater   )envs    r   _subprocess_envr%   '   s#    
**//
CJJ|Jr   c                $    V ^8  d   QhR\         /# )r   msgr   )r   s   "r   r   r   .   s      c r   c                    \         P                  P                  RV  R24       \         P                  P                  4        R# )zLog to stderr.z[Bird] 
N)sysstderrwriteflush)r'   s   &r   _logr/   .   s-    JJwse2&'JJr   c                0    V ^8  d   QhR\         R\         /# )r   topicr   r(   )r   s   "r   r   r   4   s     69 69 69 69r   c                T   V P                  4       P                  4       p. RIOpV F?  pVP                  VR,           4      '       g   K#  V\        V4      R P                  4       p M	  . RJOpV F@  pVP	                  RV,           4      '       g   K#  VR\        V4      )  P                  4       p M	  0 RkRkRkRkRkRkR	kR
kRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkRkR kR!kR"kR#kR$kR%kR&kR'kR(kR)kR*kR+kR,kR-kR.kR/kR0kR1kR2kR3kR4kR5kR6kR7kR8kR9kR:kR;kR<kR=kR>kR?kR@kRAkRBkRCkRDkREkRFkRGkpVP                  4       pV Uu. uF  qV9  g   K  VNK  	  p	pRP                  V	RH,          4      ;'       g    V P                  4       P                  4       # u upi )Ku   Extract core subject from verbose query for X search.

X search is literal keyword AND matching — all words must appear.
Aggressively strip question/meta/research words to keep only the
core product/concept name (2-3 words max).
 Naantheisarewaswereandorofinonforwithabouttopeoplesayingthinksaidlatelybesttopgoodgreatawesomekillerlatestnewnewsr#   updates	trendiesttrendinghottesthotpopularviral	practicesfeaturesguidetutorialrecommendationsadvicereviewreviewsusecasesexamples
comparisonversusvspluginpluginsskillskillstooltoolspromptprompts	prompting
techniquestipstricksmethods
strategies
approachesusingusesuse:N   N)zwhat are the bestzwhat is the bestzwhat are the latestzwhat are people saying aboutzwhat do people think aboutzhow do i usez
how to usezhow tozwhat arezwhat isztips forzbest practices for)zbest practicesz	use caseszprompt techniqueszprompting techniqueszprompting tips)lowerstrip
startswithlenendswithsplitjoin)
r1   textprefixespsuffixess_noisewordswresults
   &         r   _extract_core_subjectr   4   s    ;;= DH ??1s7##A=&&(D H ==q!!#a&>'')D  %',.46;=A 	     "( *1 37 		 	 $	 &,	 .6	 	    ' )2 4< 	    "* ,5 	   "+ -2 4= ?F 	   ") +5 	 $ &. 09 	  !- /7 9= 	  % '/ 17 9@ 	  ) +7 9? 	  * ,8" 	#" #" #F& JJLE2A6/aaF288F2J885;;=#6#6#88 3s   F%F%c                $    V ^8  d   QhR\         /# r   bool)r   s   "r   r   r   m   s     , ,4 ,r   c                 j    \         P                  4       '       g   R# \        P                  ! R4      RJ# )z|Check if vendored Bird search module is available.

Returns:
    True if bird-search.mjs exists and Node.js 22+ is in PATH.
FnodeN)_BIRD_SEARCH_MJSexistsshutilwhich r   r   is_bird_installedr   m   s*     ""$$<<t++r   c                :    V ^8  d   QhR\         \        ,          /# r   r   )r   s   "r   r   r   x   s      x} r   c                    \        4       '       g   R#  \        P                  ! R\        \        4      R.RR^\        4       R7      p V P                  ^ 8X  dQ   V P                  P                  4       '       d1   V P                  P                  4       P                  R4      ^ ,          # R#   \        P                  \        \        P                  3 d     R# i ; i)zCheck if X credentials are available (env vars or browser cookies).

Returns:
    Auth source string if authenticated, None otherwise.
Nr   z--whoamiT)capture_outputr   timeoutr$   r*   )r   
subprocessrunr   r   r%   
returncodestdoutrz   r~   TimeoutExpiredFileNotFoundErrorSubprocessError)r   s    r   is_bird_authenticatedr   x   s     S)*J7!
 !fmm&9&9&;&;==&&(..t4Q77%%'8*:T:TU s   A#B* 8/B* **CCc                $    V ^8  d   QhR\         /# r   r   )r   s   "r   r   r      s     + +T +r   c                 2    \         P                  ! R4      RJ# )zCheck if npm is available (kept for API compatibility).

Returns:
    True if 'npm' command is available in PATH, False otherwise.
npmN)r   r   r   r   r   check_npm_availabler      s     <<d**r   c                F    V ^8  d   QhR\         \        \        3,          /# r   )r   r   r   )r   s   "r   r   r      s     
N 
NeD#I& 
Nr   c                 x    \        4       '       d   R# \        P                  ! R4      '       g   R# RR\         23# )zlNo-op - Bird search is vendored in v2.1, no installation needed.

Returns:
    Tuple of (success, message).
r   Fz&Vendored bird-search.mjs not found at )TzFBird search is bundled with /last30days v2.1 - no installation needed.)Fz<Node.js 22+ is required for X search. Install Node.js first.)r   r   r   r   r   r   r   install_birdr      s=     ]]<<TT:;K:LMMMr   c                F    V ^8  d   QhR\         \        \        3,          /# r   )r   r   r   )r   s   "r   r   r      s      c3h r   c                 V    \        4       p V '       d   \        4       MRpRV RVRJRVRR/# )ztGet comprehensive Bird search status.

Returns:
    Dict with keys: installed, authenticated, username, can_install
N	installedauthenticatedusernamecan_installT)r   r   )r   auth_sources     r   get_bird_statusr      s>     "#I-6')DK 	YD0Kt	 r   c          
      j    V ^8  d   QhR\         R\        R\        R\        \         \        3,          /# )r   querycountr   r   )r   intr   r   )r   s   "r   r   r      s6     D. D.C D. D.c D.d38n D.r   c           	        R\        \        4      V R\        V4      R.p\        \        R4      '       d   \        P                  MRp \
        P                  ! V\
        P                  \
        P                  RV\        4       R7      p ^ RI	H
pHp V! VP                  4        VP                  VR	7      w  r  ^ RI	Hp T! TP                  4       TP4                  ^ 8w  d!   T	'       d   T	P7                  4       MRp
R
T
R. /# T'       d   TP7                  4       MRpT'       g   R. /# \8        P:                  ! T4      #   \         d     Li ; i  \
        P                   d     \        P                   ! \        P"                  ! TP                  4      \$        P&                  4       M,  \(        \*        \,        3 d    TP/                  4         Mi ; iTP1                  ^R	7       R
RT R2R. /u  ^ RI	Hp T! TP                  4       #   \        \2        3 d     # i ; ii ; i  \        \2        3 d     ELji ; i   ^ RI	Hp T! TP                  4       i   \        \2        3 d     i i ; i; i  \8        P<                   d   pR
RT 2R. /u Rp?# Rp?i\2         d   pR
\        T4      R. /u Rp?# Rp?ii ; i)zRun a search using the vendored bird-search.mjs module.

Args:
    query: Full search query string (including since: filter)
    count: Number of results to request
    timeout: Timeout in seconds

Returns:
    Raw Bird JSON response or error dict.
r   --count--jsonsetsidNT)r   r,   r   
preexec_fnr$   )register_child_pidunregister_child_pidr   errorzSearch timed out after r   items)r   zBird search failed zInvalid JSON response: )r   r   hasattrr    r   r   PopenPIPEr%   
last30daysr   r   pidImportErrorcommunicater   killpggetpgidsignalSIGTERMProcessLookupErrorPermissionErrorOSErrorkillwait	Exceptionr   rz   jsonloadsJSONDecodeError)r   r   r   cmdpreexecprocr   r   r   r,   r   outputes   &&&          r   _run_bird_searchr      s[    	$%3u:	C #2x00biidG/.????!
	Ktxx(	!--g->NFF;$TXX. ??a&,FLLN2FEUGR00#)rR= zz&!!9  		
 (( 	P		"**TXX.?&A 		IIaI 6wiqA7BOO;$TXX.+ 	P  + ;$TXX.+   E21#6DD .Q"--.s(  A I5 	D? $E :H( I5 +I5 I5 I5 %I5 )I5 ?E
I5 EI5 H%&AF*)H%*&GH%GH%2I  4HH!I5  H!!I5 $H%%I  (H=9I5 <H==I5  I2II2I/,I2.I//I22I5 5J?
JJ?J?%J?&J:4J?:J?c                v    V ^8  d   QhR\         R\         R\         R\         R\        \         \        3,          /# )r   r1   	from_dateto_datedepthr   )r   r   r   )r   s   "r   r   r      sB     5 555 5 	5
 
#s(^5r   c                   \         P                  V\         R,          4      pVR8X  d   ^M
VR8X  d   ^-M^<p\        V 4      pV RV 2p\        RV 24       \	        WtV4      p\        V4      p	VP                  4       p
V	'       gY   \        V
4      ^8  dI   RP                  V
R,          4      p\        RV RV R	24       V RV 2p\	        WtV4      p\        V4      p	V	'       gd   V
'       d\   0 RmpV
 Uu. uF  qV9  g   K  VNK  	  ppV'       d7   \        V\        R
7      p\        RV RV R	24       V RV 2p\	        WtV4      pV# u upi )a:  Search X using Bird CLI with automatic retry on 0 results.

Args:
    topic: Search topic
    from_date: Start date (YYYY-MM-DD)
    to_date: End date (YYYY-MM-DD) - unused but kept for API compatibility
    depth: Research depth - "quick", "default", or "deep"

Returns:
    Raw Bird JSON response or error dict.
r   r
    since:zSearching: r3   :Nr   Nz0 results for 'z', retrying with '')keyz"', retrying with strongest token '>   rV   rP   rJ   rI   rj   rh   rk   rX   rO   rf   ri   rU   rg   rW   rT   rS   )
DEPTH_CONFIGgetr   r/   r   parse_bird_responser~   r|   r   max)r1   r   r   r   r   r   
core_topicr   responser   
core_wordsshorter
low_signalr   
candidates	strongests   &&&&            r   search_xr      s_   " UL$;<EW$b0B"G 'u-Jl')-E;ug	g6H  )E !!#JS_q(((:b>*zl*<WIQGH)79+.#E':#H- Z


 ",CA
/Baa
CJC0I?:,.PQZP[[\]^ k4E'g>HO Ds   5E
E
c                    V ^8  d   QhR\         \        ,          R\        \        ,          R\        R\        R\         \        \        \
        3,          ,          /# )r   handlesr1   r   	count_perr   )r   r   r   r   r   r   )r   s   "r   r   r   6  sV     L L#YLC=L L 	L
 
$sCx.Lr   c           	        . pV'       d   \        V4      MRpV  EFX  pVP                  R4      pV'       d   RV RV RV 2pMRV RV 2pR\        \        4      VR\        V4      R.p\	        \
        R	4      '       d   \
        P                  MRp	 \        P                  ! V\        P                  \        P                  R
V	R7      p
 V
P                  ^R7      w  rT
P.                  ^ 8w  d-   \-        RT RT;'       g    RP1                  4        24       EK  T;'       g    RP1                  4       pT'       g   EK&  \2        P4                  ! T4      p\7        T4      pTP9                  T4       EK[  	  V#   \        P                   d     \
        P                  ! \
        P                  ! T
P                  4      \        P                   4       M,  \"        \$        \&        3 d    T
P)                  4         Mi ; iT
P+                  ^R7       \-        RT 24        EK	  i ; i  \2        P:                   d    \-        RT 24        EK4  \<         d   p\-        RT RT 24        Rp?EKV  Rp?ii ; i)a  Search specific X handles for topic-related content.

Runs targeted Bird searches using `from:handle topic` syntax.
Used in Phase 2 supplemental search after entity extraction.

Args:
    handles: List of X handles to search (without @)
    topic: Search topic (core subject), or None for unfiltered search
    from_date: Start date (YYYY-MM-DD)
    count_per: Results to request per handle

Returns:
    List of raw item dicts (same format as parse_bird_response output).
N@zfrom:r3   r   r   r   r   r   T)r   r,   r   r   r   zHandle search timed out for @zHandle search failed for @z: r   z%Invalid JSON from handle search for @zHandle search error for @)r   lstripr   r   r   r    r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r/   r   rz   r   r   r   extendr   r   )r   r1   r   r   	all_itemsr   handler   r   r   r   r   r,   r   r   r   r   s   &&&&             r   search_handlesr   6  s'   ( I16&u-DJs#F81ZL	{CEF879+6E C()s9~	
  'r844"))$#	<##!!"D	!%!1!1"!1!= !#1&V\\r<P<P<R;STUll))+Fzz&)H'1EU#] j 5 ,,  IIbjj2FNNC*OWE  IIK 		!	$4VH=>* ## 	C8ABB 	<,VHBqc:;;	<s   7H'
E9"H'H'H'$H' 2H'9H$AGH$&G<9H$;G<<#H$H'#H$$H''#I5I5I5I00I5c                    V ^8  d   QhR\         \        \        3,          R\        \         \        \        3,          ,          /# )r   r   r   )r   r   r   r   )r   s   "r   r   r     s3     W W$sCx. WT$sCx.5I Wr   c                   . pRV 9   d'   V R,          '       d   \        RV R,           24       V# \        V \        4      '       d   T M!V P                  RV P                  R. 4      4      p\        V\        4      '       g   V# \	        V4       EF  w  r4\        V\
        4      '       g   K  VP                  R4      ;'       g    VP                  RR4      pV'       g   VP                  R4      '       do   VP                  R	/ 4      ;'       g    VP                  R
/ 4      pVP                  R4      ;'       g    VP                  RR4      pV'       d   RV RVR,           2pV'       g   K  RpVP                  R4      ;'       g    VP                  RR4      p	V	'       do    \        V	4      ^
8  d6   V	^
,          R8X  d(   \        P                  ! V	P                  RR4      4      p
M\        P                  ! V	R4      p
V
P                  R4      pVP                  R	/ 4      ;'       g    VP                  R
/ 4      pVP                  R4      ;'       g-    VP                  RR4      ;'       g    VP                  RR4      pRVP                  R4      ;'       g+    VP                  R4      ;'       g    VP                  R4      RVP                  R4      ;'       g    VP                  R4      RVP                  R 4      ;'       g    VP                  R!4      R"VP                  R#4      ;'       g    VP                  R$4      /pV F#  pW,          f   K   \        W,          4      W&   K%  	  RR%V^,            2R&\        VP                  R&VP                  R'R4      4      4      P!                  4       R(,          RTRVP#                  R)4      R*TR+\$        ;QJ d*    R, VP'                  4        4       F  '       g   K   R-M	  R.M! R, VP'                  4        4       4      '       d   TMRR/RR0R1/pVP)                  V4       EK  	  V#   \        \        3 d     ELJi ; i  \        \        3 d
    RY&    EK1  i ; i)2zParse Bird response to match xai_x output format.

Args:
    response: Raw Bird JSON response

Returns:
    List of normalized item dicts matching xai_x.parse_x_response() format.
r   zBird error: r   tweetspermanent_urlurlr   idauthoruserr   screen_namezhttps://x.com/z/status/N	createdAt
created_atTZz+00:00z%a %b %d %H:%M:%S %z %Yz%Y-%m-%dauthor_handlelikes	likeCount
like_countfavorite_countrepostsretweetCountretweet_countreplies
replyCountreply_countquotes
quoteCountquote_countXr   	full_text:Ni  Nr   date
engagementc              3   (   "   T F  qR Jx  K
  	  R # 5i)Nr   ).0vs   & r   	<genexpr>&parse_bird_response.<locals>.<genexpr>  s     +WCVaTMCVs   TFwhy_relevant	relevancegffffff?)r/   
isinstancelistr   	enumeratedictr|   r   fromisoformatreplacestrptimestrftime
ValueError	TypeErrorr   r   rz   r   anyvaluesappend)r   r   	raw_itemsitweetr   r   r   r  r  dtr  r  r   items   &              r   r   r     s    E (x00|HW-./0 'x66HLLRZR^R^_gikRl<mIi&&i(%&& ii(@@EIIeR,@uyyYYx,EE		&"0EF **Z0QQFJJ}b4QK&{m8E$K=I YY{+JJuyyr/J

 z?R'JrNc,A!//
0B0B30QRB "**:7PQB{{:.
 8R(AAEIIfb,A

:.qq&**]B2OqqSXS\S\]lnpSq UYY{+eeuyy/Fee%))TdJeuyy0NNEIIo4Nuyy.JJ%))M2Jeii-II=1I	

 C*+&)*/&:JO  AacU)C		&%))K*DEFLLNtT3]11#6D+W:CTCTCV+W+W:CTCTCV+W(W(W*]aB	
 	T )B LI 	* & #I. +&*JO+s%   #A-QQQQQ87Q8c                b    V ^8  d   Qh/ ^ \         9   d   \        \        \        3,          ;R&   # )r   r   )__conditional_annotations__r   r   )r   s   "r   r   r      s&      6 " !d38n !7r   )r   )   )$r0  __doc__r   r    r   r   r   r+   pathlibr   r   typingr   r   r   r   r   __file__parentr   r   r   r   r%   r/   r   r   r   r   r   r   r   r   r   r   r   )r0  s   @r   <module>r7     s     	    
   3 3 >((83mCFWW  Rr
B  " !"69r,0+
N"D.N5pL^Wr   