
    `gj*/                       d Z ddlm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mZ  ej                  e      Zg dZ G d d	e      Ze G d
 d             ZddZddZddZdd	 	 	 	 	 ddZddd	 	 	 	 	 	 	 ddZddZddddZddZy)u  Blueprints: shareable plain-language automations layered on skills + cron.

A "blueprint" is NOT a new object type. It is an ordinary skill (a SKILL.md the
agent loads) that additionally declares an automation schedule in its
frontmatter:

    metadata:
      hermes:
        blueprint:
          schedule: "0 9 * * *"     # presence of `blueprint:` marks it runnable
          deliver: origin            # optional (default "origin")
          prompt: "..."              # optional task instruction for the run
          no_agent: false            # optional

Because a blueprint is just a skill, it flows through the ENTIRE existing
skills-hub pipeline for free — search, inspect, quarantine, security scan,
install, lock-file provenance, audit log, taps, the centralized index, and
`hermes skills publish` for sharing. No new source type, no new store, no new
transport. This module is the thin bridge between that skill metadata and the
existing cron `create_job()` API:

  * ``parse_blueprint(skill_md_text)``  -> BlueprintSpec | None
  * ``blueprint_spec_for_installed(name)`` -> BlueprintSpec | None
  * ``create_blueprint_job(spec, ...)`` -> the created cron job dict
  * ``export_blueprint(job, body)``      -> a shareable SKILL.md string

The dev guide's "Extend, Don't Duplicate" rule is the whole design: the blueprint
is a skill, the schedule is a cron job, sharing is the existing publish/tap/
index path.
    )annotationsN)	dataclassfield)Path)AnyDictListOptional)BlueprintSpecparse_blueprintblueprint_spec_for_installedblueprint_to_job_speccreate_blueprint_jobregister_blueprint_suggestionexport_blueprintBlueprintErrorc                      e Zd ZdZy)r   z7Raised when a blueprint block is present but malformed.N)__name__
__module____qualname____doc__     C/root/.hermes/venv/lib/python3.12/site-packages/tools/blueprints.pyr   r   5   s    Ar   r   c                      e Zd ZU dZded<   ded<   dZded<   dZded	<   d
Zded<   dZded<   dZ	ded<   dZ
ded<    ee      Zded<   y)r   zAParsed ``metadata.hermes.blueprint`` automation spec for a skill.str
skill_namescheduleorigindeliverNOptional[str]promptFboolno_agentmodelproviderzOptional[List[str]]enabled_toolsets)default_factoryDict[str, Any]raw)r   r   r   r   __annotations__r    r"   r$   r%   r&   r'   r   dictr*   r   r   r   r   r   9   s\    KOMGS FM HdE="Hm",0)05C5r   r   c                t   t        | t              sy| j                  d      j                         }|j                  d      sy|dd }|j	                  d      }|dk(  ry|d| }	 ddl}|j                  |      }t        |t              r|S dS # t        $ r }t        j                  d|       Y d}~yd}~ww xY w)	zFReturn the parsed YAML frontmatter mapping, or None if absent/invalid.Nu   ﻿z---   z
---r   z,blueprint: frontmatter YAML parse failed: %s)
isinstancer   lstrip
startswithfindyaml	safe_load	Exceptionloggerdebugr,   )textstripped
after_openendfm_textr4   dataes           r   _split_frontmatterr@   H   s    dC {{8$++-Hu%!"J
//'
"C
by#G~~g& dD)43t3  CQGs   %B 	B7B22B7c                   t        |       }|syt        |j                  dd            j                         }|j                  d      }t	        |t
              r|j                  d      nd}t	        |t
              r|j                  d      nd}|yt	        |t
              st        d      t        |j                  dd            j                         }|st        d	      t        |j                  d
d            j                         xs d}|j                  d      }|t        |      }t        |j                  dd            }	|j                  d      }
|j                  d      }|j                  d      }|t	        |t              st        d      t        |||||	|
rt        |
      j                         nd|rt        |      j                         nd|r|D cg c]  }t        |       c}|	      S d|	      S c c}w )aB  Extract a BlueprintSpec from a SKILL.md string, or None if not a blueprint.

    A skill is a blueprint iff ``metadata.hermes.blueprint`` is a mapping containing
    a non-empty ``schedule``. Raises BlueprintError if the block exists but is
    structurally invalid (so a typo surfaces instead of silently no-op'ing).
    Nname metadatahermes	blueprintz+metadata.hermes.blueprint must be a mappingr   z4blueprint.schedule is required and must be non-emptyr    r   r"   r$   Fr%   r&   r'   z6blueprint.enabled_toolsets must be a list when present)	r   r   r    r"   r$   r%   r&   r'   r*   )
r@   r   getstripr0   r,   r   r#   listr   )skill_md_textfmrB   metarE   rF   r   r    r"   r$   r%   r&   toolsetsts                 r   r   r   _   s    
M	*Brvvfb!"((*D66*D#-dD#9TXXhtF+5fd+C

;'Ii&JKK9==R01779HSTT)--	845;;=IG]]8$FVIMM*e45HMM'"E}}Z(H}}/0HJx$>UVV$)c%j t*2X$$&7?(3Q#a&3
 
 FJ
 
 4s   !Hc                   	 ddl m} t        |      }t	        |j                  d|  d            }|D ]9  }	 |j                  d      }t        |      }|$|j                  s| |_	        |c S  y# t        $ r Y yw xY w# t        $ r Y Ww xY w)zLocate an installed skill's SKILL.md and parse its blueprint block.

    Searches the standard skills tree for ``<skill_name>/SKILL.md``. Returns
    None if the skill isn't found or isn't a blueprint.
    r   )
SKILLS_DIRNz**/z	/SKILL.mdzutf-8)encoding)
tools.skills_hubrP   r6   r   rI   glob	read_textOSErrorr   r   )r   rP   base
candidatespathr9   specs          r   r   r      s    / 
Ddii#j\ ;<=J 
	>>7>3D t$??",K
 #    		s"   A0 A?0	A<;A<?	B
BrB   c          
         | j                   | j                  |xs d| j                   | j                  | j                  r| j                  gnd| j                  | j
                  | j                  | j                  d	S )a}  Build the ``cron.jobs.create_job`` kwargs dict for a BlueprintSpec.

    This is the single source of truth for translating a blueprint into a job.
    Both the direct ``create_blueprint_job`` path and the suggestion path
    (``register_blueprint_suggestion``) build on it, so a blueprint scheduled now and
    a blueprint accepted from a suggestion produce an identical job.
    
blueprint:N)	r"   r   rB   r    skillsr%   r&   r'   r$   )r"   r   r   r    r%   r&   r'   r$   )rY   rB   s     r   r   r      sg     ++MM6*T__$56<<'+4??#DMM 11MM
 
r   )r   rB   c               F    ddl m} t        | |      }|||d<    |di |S )a'  Create the cron job described by a BlueprintSpec via the existing cron API.

    The blueprint's skill is loaded before the run (cron ``skills=[name]``); the
    optional ``prompt`` becomes the task instruction. Delivery, model, and
    toolsets carry through. Returns the created job dict.
    r   )
create_jobrZ   r   r   )	cron.jobsr_   r   )rY   r   rB   r_   job_specs        r   r   r      s1     %$T5H#!!!r   c           
     X   | j                   sy	 ddlm}  |d| j                    dd| j                    d| j                   | j
                  r| j
                  dk7  rd	| j
                   nd
z   dz   dt        |       d| j                    d| j                         S # t        $ r Y yw xY w)u  Turn an installed blueprint into a pending Suggested Cron Job.

    Blueprints are source ``blueprint`` of the unified suggestion surface: installing
    a skill that carries a ``blueprint:`` block does NOT auto-schedule it — it
    registers a suggestion the user accepts (or dismisses) like any other.
    Returns the suggestion record, or None if it was skipped (already
    seen/dismissed, backlog full, etc.).
    Nr   )add_suggestionz
Schedule ''zThe 'z' blueprint runs on schedule r   z, delivering to rC   .rF   r\   :)titledescriptionsourcera   	dedup_key)r   cron.suggestionsrc   r6   r   r    r   )rY   rc   s     r   r   r      s     ??3 4??+1-DOO$$A$--Q48LLT\\U]E]!$,,0ceg &t,t/q@
 
  s   B 	B)(B))blueprint_namec                  ddl }|xs | j                  d      xs d}dj                  d t        |      j	                         D              }|j                  d      xs d}| j                  d      xs t        | j                  d	            }| j                  d
      xs | j                  d      r| d   gng }d	|i}| j                  d      }|r
|dk7  r||d<   | j                  d      r| d   |d<   | j                  d      rd|d<   | j                  d      r| d   |d<   | j                  d      r| d   |d<   | j                  d      r| d   |d<   |j                         r)|j                         j                         xs dgd   dd nd}	||	dddddg|did}
|j                  |
dd      j                         }|j                         xs d| d }d!| d"| d#S )$ax  Render a shareable blueprint SKILL.md from an existing cron job dict.

    The inverse of ``create_blueprint_job``: take a cron job a user already built
    and emit a SKILL.md (with a ``metadata.hermes.blueprint`` block) they can hand
    to ``hermes skills publish`` to share. ``body`` is the plain-language
    description / instructions that become the SKILL.md body.
    r   NrB   zshared-blueprintrC   c              3  L   K   | ]  }|j                         s|d v r|nd  yw)-_-N)isalnum).0cs     r   	<genexpr>z#export_blueprint.<locals>.<genexpr>  s#     WT	1;Ws   "$ro   schedule_displayr   r]   skillr    r   r"   r$   Tr%   r&   r'   zShared automation blueprint.   z1.0.0MITrE   rF   
automation)tagsrF   )rB   rh   versionlicenserD   F)	sort_keysallow_unicodez# z

Shared automation blueprint.z---
z
---


)	r4   rG   joinr   lowerrH   _schedule_to_string
splitlines	safe_dump)jobbodyrl   r4   rB   r   r]   blueprint_blockr    rh   frontmatterfm_yaml	body_texts                r   r   r      s     BSWWV_B0BD77WSY__EVWWD::d11Dww)*V.A#''*BU.VHWWXNSWWW5ECL>2F'18&<Oggi G7h&%,	"
wwx$'M!
wwz&*
#
www#&w< 
wwz&)*o
#
ww!".12D.E*+ ::< 
	 	 	"	F'E&FJ4CP+  "$l3,
K nn[EnNTTVG

K"TF*J KI7)9YKr22r   c                   t        | t              r| S t        | t              r| j                  d      }|dk(  r| j                  d      rt        | d         S |dk(  r}| j                  d      r%t	        | d         }|dz  dk(  r	d|dz   d	S d| d
S | j                  d      r6t	        | d         }|dz  dk(  r	d|dz   d	S |dz  dk(  r	d|dz   d
S d| dS y)z>Best-effort render of a parsed schedule dict back to a string.kindcronexprintervalminutes<   r   zevery hmsecondsi  sz	0 9 * * *)r0   r   r,   rG   int)r   r   minssecss       r   r   r   .  s   (C (D!||F#6>hll62x'((: ||I&8I./"9>#DBJ<q11vQ''||I&8I./$;!##DDL>33"9>#DBJ<q11vQ''r   )r9   r   returnOptional[Dict[str, Any]])rJ   r   r   Optional[BlueprintSpec])r   r   r   r   )rY   r   rB   r!   r   r)   )rY   r   r   r   rB   r!   r   r)   )rY   r   r   r   )r   r)   r   r   rl   r!   r   r   )r   r   r   r   )r   
__future__r   loggingdataclassesr   r   pathlibr   typingr   r   r	   r
   	getLoggerr   r7   __all__
ValueErrorr   r   r@   r   r   r   r   r   r   r   r   r   r   <module>r      s   > #  (  , ,			8	$	BZ B 6 6 64..b> 
  	8 (,	"
" %" 	"
 "(: Y] 53pr   