
    9i"                    6   d Z ddlm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mZ ddlmZ ddlmZmZ  ee      j'                         j(                  d   Zedz  Ze
 G d	 d
             Z G d d      Z G d d      Z G d de      Z G d de      Zy)u0  skills/_lib/contract.py — Skill interface + Registry + helpers.

Cualquier skill nueva crea un módulo `skills/<name>.py` con UNA función:

    def run(inputs: dict, context: SkillContext) -> SkillResult: ...

Donde:
    SkillContext = ejecuta-time helpers (caja, shared_state, run_dir, claude CLI helper).
    SkillResult  = {ok: bool, output: dict, errors: list[str], metadata: dict}

El registry auto-descubre todas las skills bajo `skills/*.py` (skip _lib y __init__).

Patrón canónico: cada skill ELIGE su backend internamente:
  - Python puro (yt-dlp / faster-whisper / Trello API / file ops)
  - claude CLI subprocess (LLM via plan Max — sin API key)
  - HTTP / API externa (Apify / GetHookd / etc)
  - Browser CDP (Playwright)

El orchestrator NO conoce los backends. Solo invoca la skill por nombre.
    )annotationsN)	dataclassfieldasdict)Path)AnyCallable   skillsc                  t    e Zd ZU ded<    ee      Zded<    ee      Zded<    ee      Z	ded<   dd	Z
y
)SkillResultboolok)default_factorydictoutput	list[str]errorsmetadatac                    t        |       S N)r   selfs    EC:\Users\ferna\proyecto cero\_factory_3d_demo\skills\_lib\contract.pyto_dictzSkillResult.to_dict/   s    d|    Nreturnr   )__name__
__module____qualname____annotations__r   r   r   listr   r   r    r   r   r   r   (   s9    H.FD.d3FI340Hd0r   r   c                  T    e Zd ZdZd	d
dZddZddZdddZ	 	 d	 	 	 	 	 ddZddZ	y)SkillContextu   Helpers que cada skill puede usar para tocar caja, shared_state, claude CLI, etc.

    NO permite que la skill toque estado de OTRAS skills directamente — solo escribir
    su sección en la caja vía `context.write_caja(section, value, mode)`.
    Nc                J    || _         || _        || _        || _        || _        y r   )flow_idrun_dir
station_idcajashared_state_path)r   r(   r)   r*   r+   r,   s         r   __init__zSkillContext.__init__>   s&    $	!2r   c                6    | j                   j                         S r   )r+   	read_fullr   s    r   	read_cajazSkillContext.read_cajaE   s    yy""$$r   c                8    | j                   j                  |      S r   )r+   read_section)r   sections     r   r2   zSkillContext.read_sectionH   s    yy%%g..r   c                b    |xs | j                   }| j                  j                  ||||       y )N)modeactor)r*   r+   write_section)r   r3   valuer5   r6   s        r   r7   zSkillContext.write_sectionK   s+    (		TGr   c                   ddd|g}|dk(  r|ddgz  }|r|d|gz  }|j                  |       	 t        j                  |ddd|	      }|j                  d
k(  |j                  |j                  |j
                  d}|dk(  r4|j                  d
k(  r%	 t        j                  |j                        |d<   |S |S # t        $ r}	t        |	      |d<   Y d}	~	|S d}	~	ww xY w# t        j                  $ r dd| ddddcY S t        $ r
 dddddcY S w xY w)u  Invoca el binario `claude` CLI vía subprocess. Usa el plan Max — sin API key.

        Args:
            prompt: el contenido a procesar.
            model: claude-opus-4-7 default (REGLA msg 4658 Fer 2026-05-09).
            system: system prompt opcional.
            output_format: 'text' | 'json' (claude CLI flag).
            timeout_s: máximo en segundos.

        Returns:
            {ok, stdout, stderr, returncode, parsed_json (si output_format=json)}
        claudez--printz--modeljsonz--output-formatz--system-promptTutf-8)capture_outputtextencodingtimeoutr   )r   
returncodestdoutstderrparsed_jsonparse_errorNFztimeout s )r   errorrB   rC   z claude CLI no encontrado en PATH)append
subprocessrunrA   rB   rC   r;   loads	ExceptionstrTimeoutExpiredFileNotFoundError)
r   promptmodelsystemoutput_format	timeout_scmdrresultes
             r   
claude_clizSkillContext.claude_cliO   s?    Iu5F"%v..C%v..C

6	js4dW'02A lla'll((((	F &1<<1+<3,0JJqxx,@F=) M6M ! 3,/FF=)M3 (( 	_HYKq*AR[]^^  	j*LXZfhii	jsH   A C "B5 1C 3C 5	C>CC CC D:DDc           	        | j                   sy	 ddlm}  || j                         }|j                  ||      S # t        $ r3 | j                   }|j                  j                  dd       |j                         s-|j                  t        j                  i g g ddd      d	
       t        j                  |j                  d	
            }||j                  dg       v s||j                  dg       v rY y|j                  dg       j                  |       |j                  di       j                  |g       j                  |       |j                  t        j                  |dd      d	
       Y yw xY w)u   Intenta tomar un claim del shared_state. Útil para parallel clusters.

        Returns True si pudo tomar, False si ya estaba tomado.
        Tr   )SharedState)parentsexist_ok)claimsqueries_in_progressqueries_doneFr
   )ensure_asciiindentr<   )r?   r`   ra   r_   )r,   orchestrator_v2r\   ImportErrorparentmkdirexists
write_textr;   dumpsrL   	read_textget
setdefaultrI   
take_query)r   claim_idagent_idr\   sspdsss          r   shared_state_takezSkillContext.shared_state_takew   sS   
 %%	3 //0}}Xx00  	((CJJTD9::<tzzRPRdf*gv{  EF   G  RY  Z

3=='=:;A155!6;;x155Q_acKd?dLL.3::8DLL2&11(B?FFxPNN4::aeAFQXNY	s   9 B=E68A;E65E6r   )r(   rN   r)   r   r*   rN   r,   zPath | Noner   )r3   rN   r   r   )replacerG   )r3   rN   r8   r   r5   rN   r6   rN   )zclaude-opus-4-7Nr>   i,  )rQ   rN   rR   rN   rS   z
str | NonerT   rN   rU   intr   r   )ro   rN   rp   rN   r   r   )
r   r    r!   __doc__r-   r0   r2   r7   rZ   rt   r$   r   r   r&   r&   7   sM    3%/H \`AD&j"%&j;>&jIM&jP1r   r&   c                  4    e Zd ZdZefddZddZd	dZd
dZy)SkillRegistryzKCarga y cachea skills bajo skills/<name>.py. Cada skill DEBE exponer `run`.c                     || _         i | _        y r   )
skills_dir_cache)r   r{   s     r   r-   zSkillRegistry.__init__   s    $+-r   c                    g }| j                   j                  d      D ]H  }|j                  j                  d      s|j                  dk(  r.|j                  |j                         J t        |      S )z*Devuelve lista de skill names disponibles.z*.py_r-   )r{   globname
startswithstemrI   sorted)r   namesps      r   discoverzSkillRegistry.discover   s\    %%f-Avv  %:)=LL  . e}r   c                z   || j                   v r| j                   |   S | j                  | dz  }|j                         st        d| d| d      t        j
                  j                  d| |      }|r|j                  st        d|       t        j
                  j                  |      }|t        j                  d| <   	 |j                  j                  |       t        |d
d	      }t        |      st        d| d      || j                   |<   |S # t        $ r}t        d| d|       |d	}~ww xY w)z#Carga una skill por nombre. Cachea.z.pyzSkill no encontrada: z (esperaba )zskills.zNo se pudo cargar spec de zError cargando : NrK   Skill u(    no expone función run(inputs, context))r|   r{   rh   SkillNotFound	importlibutilspec_from_file_locationloaderSkillLoadErrormodule_from_specsysmodulesexec_modulerM   getattrcallable)r   
skill_namepy_pathspecmodrY   run_fns          r   loadzSkillRegistry.load   s@   $;;z**//zl#$66~~"7
|;wiWX YZZ~~55
|6LgV4;; #=gY!GHHnn--d3.1gj\*+	MKK##C( eT* 6*5]!^__"(J  	M ?:,b!DE1L	Ms   D 	D:#D55D:c           
     b   	 | j                  |      }	  |||      }t        |t              r|S t        |t              rXt        t        |j                  dd            |j                  di       |j                  d	g       |j                  d
i             S t        dd| dt        |      j                   g      S # t        t        f$ r!}t        dt	        |      g      cY d}~S d}~ww xY w# t
        $ r4}t        dd| dt        |      j                   d| g      cY d}~S d}~ww xY w)uC   Invoca una skill. Convierte excepción en SkillResult con ok=False.F)r   r   Nr   z
 crashed: r   r   r   r   r   )r   r   r   r   u    devolvió tipo no esperado: )r   r   r   r   rN   rM   typer   
isinstancer   r   rl   )r   r   inputscontextr   rY   ress          r   invokezSkillRegistry.invoke   s+   	:YYz*F	j)C c;'Jc4 e,-wwx,wwx,R0	  evj\A^_cdg_h_q_q^r-s,tuu! ~. 	:%Q99	:  	j%6*ZPTUVPWP`P`Oaacdecf1g0hii	js:   B> 	C1 >C.C)#C.)C.1	D.:)D)#D.)D.N)r{   r   )r   r   )r   rN   r   r	   )r   rN   r   r   r   r&   r   r   )	r   r    r!   rw   
SKILLS_DIRr-   r   r   r   r$   r   r   ry   ry      s    U*4 .,vr   ry   c                      e Zd Zy)r   Nr   r    r!   r$   r   r   r   r          r   r   c                      e Zd Zy)r   Nr   r$   r   r   r   r      r   r   r   )rw   
__future__r   r   importlib.utilr;   rerJ   r   dataclassesr   r   r   pathlibr   typingr   r	   __file__resolver]   ROOTr   r   r&   ry   rM   r   r   r$   r   r   <module>r      s   ( #    	  
 0 0   H~''*H_
   V1 V1z:v :vB	I 		Y 	r   