
    sj                    @   d Z ddlm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
 d)dZd*dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd+dZd,dZd+dZd+dZd+dZd+d Zd+d!Zd+d"Zd+d#Zd-d&Zd.d+d'Z e!d(k    r ej"         e                        dS dS )/u0  CLI subcommand: `hermes curator <subcommand>`.

Thin shell around agent/curator.py and tools/skill_usage.py. Renders a status
table, triggers a run, pauses/resumes, and pins/unpins skills.

This module intentionally has no side effects at import time — main.py wires
the argparse subparsers on demand.
    )annotationsN)datetimetimezone)Path)OptionaltsOptional[str]returnstrc                   | sdS 	 t          j        |           }n&# t          t          f$ r t	          |           cY S w xY w|j         |                    t          j                  }t          j	        t          j                  |z
  }t          |                                          }|dk     r| dS |dk     r|dz   dS |dk     r|dz   dS |dz   d	S )
Nnevertzinfo<   zs agoi  zm agoQ zh agozd ago)r   fromisoformat	TypeError
ValueErrorr   r   replacer   utcnowinttotal_seconds)r   dtdeltasecss       6/home/agent/.hermes/hermes-agent/hermes_cli/curator.py_fmt_tsr      s    w#B''z"   2ww	yZZx|Z,,L&&+Eu""$$%%Dbyy~~~d{{"*####e||$,%%%%em""""s     >>Nonec                 d   ddl m}  	 |                                 }n# t          $ r Y dS w xY w|sdS t	          d |D                       }t          |          |z
  }t          dt          |           d           t          d|            t          d|            t          d	           dS )
u  Report curation-eligible skills that carry no provenance marker.

    A skill only becomes curator-managed once ``created_by: agent`` lands on
    its usage record, which happens ONLY for background-review creations.
    Skills predating that marker, plus every foreground
    ``skill_manage(create)``, are eligible but unmanaged — no automatic
    transition ever considers them. Printing just the managed count made a
    large library look fully curated while a big slice was untouchable.
    r   skill_usageNc              3  D   K   | ]}|                     d           dV  dS )has_provenance_key   Nget).0rs     r   	<genexpr>z+_print_unmanaged_summary.<locals>.<genexpr>9   s4      IIqQUU3G-H-HIIIIIII    z#
unmanaged (no provenance marker): z totalz  pre-dates marker    z  foreground-created  uP     never auto-staled or archived — `hermes curator adopt <name>` hands one over)toolsr"   unmanaged_report	Exceptionsumlenprint)r"   	unmanagedlegacy
foregrounds       r   _print_unmanaged_summaryr5   '   s     "!!!!!0022		    IIIIIIIIFY&(J	
GY
G
G
GHHH	
+6
+
+,,,	
/:
/
/000		7    s    
++r   c                (   ddl m} ddlm} |                                }|                                }|                    dd          }|                    d          }|                    d          pd}|                    d	d          }|r|sd
n|rdnd}	t          d|	            t          d|            t          dt          |                      d|v r@|	                                ^}
}t          d|
            |D ]}t          d|            nt          d|            |                    d          }|r9t          |                                          rdnd}t          d| |            |                                }|dz  dk    r|dk    r|dz   dn| d}t          d|            t          d|                                 d           t          d|                                 d           t          d|                                rdnd  |                                rdnd!            |                                }|st          d"           t#                       dS g g g d#}g }d}d}|D ]}|                    d$d%          }|                    |g                               |           |                    d&          r|                    |d'                    |                    d(d)          }|d)k    r|d*z  }|d+k    r|d*z  }t          d,t)          |           d-| d.| d/           d#D ];}|                    |g           }t          d0|d1d2t)          |                      <|r5t          d3t)          |           d4d5                    |                      t#                       t-          |                    d%g           d6 7          d d8         }|rt          d9           |D ]}t          |                    d:                    }t          d0|d'         d;d<|                    d=d          d>d?|                    d@d          d>dA|                    dBd          d>dC|                    dDd          d>dE|            |                    d%g           }|rt-          |dF dGH          d d8         }|r|d                             d=          pddk    rt          dI           |D ]}t          |                    d:                    }t          d0|d'         d;d<|                    d=d          d>d?|                    d@d          d>dA|                    dBd          d>dC|                    dDd          d>dE|            t-          |dJ 7          d d8         }|rt          dK           |D ]}t          |                    d:                    }t          d0|d'         d;d<|                    d=d          d>d?|                    d@d          d>dA|                    dBd          d>dC|                    dDd          d>dE|            dS )LNr   curatorr!   pausedFlast_run_atlast_run_summaryz(none)	run_countENABLEDPAUSEDDISABLED	curator: z  runs:           z  last run:       
z  last summary:   z                  last_report_path z
 (missing)z  last report:       dhz  interval:       every z  stale after:    zd unusedz  archive after:  z  consolidate:    onoffz$ (prune-only; LLM merge pass opt-in)z
no curator-managed skills)activestalearchivedstaterI   pinnedname
provenanceagentr%   bundledz
curator-managed skills: z total  (agent-created=
  bundled=)  10s z	
pinned (z): z, c                Z    |                      d          p|                      d          pdS )Nlast_activity_at
created_atrC   r&   r)   s    r   <lambda>z_cmd_status.<locals>.<lambda>   s)    aee.//L1553F3FL" r+   key   z
least recently active (top 5):rX   40sz  activity=activity_count3dz  use=	use_countz  view=
view_countz
  patches=patch_count  last_activity=c                ^    |                      d          pd|                      d          pdfS Nr`   r   rX   rC   r&   rZ   s    r   r[   z_cmd_status.<locals>.<lambda>   0    155!1227a?Q9R9R9XVXY r+   Tr]   reversez
most active (top 5):c                ^    |                      d          pd|                      d          pdfS rg   r&   rZ   s    r   r[   z_cmd_status.<locals>.<lambda>   rh   r+   z
least active (top 5):)rP   r8   r,   r"   
load_state
is_enabledr'   r1   r   
splitlinesr   existsget_interval_hoursget_stale_after_daysget_archive_after_daysget_consolidatecurated_reportr5   
setdefaultappendr0   joinsorted)argsr8   r"   rL   enabledr9   last_runsummaryrunsstatus_linefirstrestline_reportsuffix_ih_interval_labelrowsby_staterM   agent_countbundled_countr)   
state_nameprovbucketrI   last
active_allmost_activeleast_actives                                  r   _cmd_statusr   D   s   !!!!!!  E  ""GYYx''Fyy''Hii*++7xG99[!$$D  	V 			 	 
 

#k
#
#$$$	
%t
%
%&&&	
2wx00
2
2333 w))++*5**+++ 	/ 	/D-t--....	/ 	,7,,---ii*++G 6G}}++--?<474F44555

$
$
&
&C8q==SBYY3"9YYY  

6_
6
6777	
Gw;;==
G
G
GHHH	
Iw==??
I
I
IJJJ		YW%<%<%>%>ITTE 	Y((**V220V	Y 	Y  
 %%''D +,,, """qrr::HFKM 	 	UU7H--
J++22155555?? 	%MM!F)$$$uu\7++7??1KKYQM	 Ds4yy D D'D D3@D D D E E E5 3 3
j"--1:111CKK112222 @>3v;;>>499V+<+<>>??? 
 Xr""LL   	qb
F  0111 		 		A155!34455D(QvY& ( (EE"2A66=( (uu[!,,3( ( lA..5( ( 55229	( (
 "&( (    h++J "YY
 
 
 1"	
  	KN../?@@EAJJ*+++  	 	quu%78899,6* , , !&6 : :A, ,55a007, , EE,229, ,  !uu]A66=	, ,
 &*, ,    YY
 
 
 1"  	+,,,! 	 	quu%78899,6* , , !&6 : :A, ,55a007, , EE,229, ,  !uu]A66=	, ,
 &*, ,    1r+   c                   ddl m} |                                st          d           dS t	          t          | dd                    }t	          t          | dd                    }t	          t          | dd                    p| }t	          t          | d	d                    rd
nd }|rt          d           nt          d           |#|                                st          d           d"d}|                    ||||          }|                    di           }|r|r(t          d|                    dd           d           nkt          d|                    dd           d|                    dd           d|                    dd           d|                    dd                      |st          d           |r!|rt          d            nt          d!           dS )#Nr   r7   zAcurator: disabled via config; enable with `curator.enabled: true`r%   dry_runF
backgroundsynchronousconsolidateTz7curator: running DRY-RUN (report only, no mutations)...zcurator: running review pass...u   curator: consolidation is off — running prune-only (deterministic stale/archive). Pass --consolidate or set `curator.consolidate: true` to enable the LLM merge pass.msgr   r
   r   c                $    t          |            d S N)r1   )r   s    r   _on_summaryz_cmd_run.<locals>._on_summary   s    c




r+   )
on_summaryr   r   r   auto_transitionszauto (preview): checkedu9    candidate skill(s) — no transitions applied in dry-runzauto: checked=z stale=marked_stalez
 archived=rK   z reactivated=reactivateduF   llm pass running in background — check `hermes curator status` laterzzdry-run: no changes applied. Read the report with `hermes curator status` and run `hermes curator run` (no flag) to apply.zdry-run: no changes applied. When the report lands, read it with `hermes curator status` and run `hermes curator run` (no flag) to apply.)r   r   r
   r   )	rP   r8   rm   r1   boolgetattrrs   run_curator_reviewr'   )	ry   r8   dryr   r   r   r   resultautos	            r   _cmd_runr      s    QRRRq
wtY..
/
/CgdL%8899Jwt]E::;;M:~K wt]EBBCCM$$K
 1GHHHH/0007#:#:#<#<H	
 	
 	
    ''	 (  F ::("--D  	8488Iq#9#9 8 8 8   
 <)Q!7!7 < <.!44< < HHZ33< <  $xxq99< <    XVWWW
 
 		[   
 [   1r+   c                Z    ddl m} |                    d           t          d           dS )Nr   r7   Tzcurator: pausedrP   r8   
set_pausedr1   ry   r8   s     r   
_cmd_pauser     s<    t	
1r+   c                Z    ddl m} |                    d           t          d           dS )Nr   r7   Fzcurator: resumedr   r   s     r   _cmd_resumer     s<    u	
1r+   c                    ddl m} |                    | j                  st	          d| j         d           dS |                    | j        d           t	          d| j         d           dS )	Nr   r!   
curator: 'u`   ' is bundled or hub-installed — cannot pin (only agent-created skills participate in curation)r%   Tzcurator: pinned 'z ' (will bypass auto-transitions)r,   r"   is_agent_createdskillr1   
set_pinnedry   r"   s     r   _cmd_pinr   "  s    !!!!!!''
33 B B B B	
 	
 	
 q4:t,,,	
Jdj
J
J
JKKK1r+   c                    ddl m} |                    | j                  st	          d| j         d           dS |                    | j        d           t	          d| j         d           dS )	Nr   r!   r   ue   ' is bundled or hub-installed — there's nothing to unpin (curator only tracks agent-created skills)r%   Fzcurator: unpinned ''r   r   s     r   
_cmd_unpinr   /  s    !!!!!!''
33 R R R R	
 	
 	
 q4:u---	
-

-
-
-...1r+   c                   ddl m} |                                }|st          d           dS t          dt	          |           d           t          |d           D ]v}|                    d          rd	nd
}t          |                    d                    }t          d|d         dd|                    dd          dd|dd| d	           wt          d           dS )zList curation-eligible skills that carry no provenance marker.

    The same population `status` summarizes, itemized. Useful before deciding
    what to hand over with `adopt`.
    r   r!   u@   curator: no unmanaged skills — every eligible skill is managedzunmanaged skills (z):c                    | d         S NrN    )xs    r   r[   z%_cmd_list_unmanaged.<locals>.<lambda>J  s
    &	 r+   r\   r$   zcreated_by:nullz	no markerrX   rT   rN   44sz
 activity=r`   4dre   14sz  (rS   za
adopt one with `hermes curator adopt <name>`, or all with `hermes curator adopt --all-unmanaged`)r,   r"   r-   r1   r0   rx   r'   r   )ry   r"   r   r)   whyr   s         r   _cmd_list_unmanagedr   <  sM    "!!!!!''))D PQQQq	
,s4yy
,
,
,---D11222 
 
#$55)=#>#>OKquu/00116!  .229 !)    	
 	
 	
 	
 
 ? @ @ @1r+   c                D   ddl m} t          t          | dd          pg           }t	          t          | dd                    }|r:|rt          d           dS |                                }|st          d	           dS |st          d
           dS t	          t          | dd                    }|r9t          dt          |           d           |D ]}t          d|            dS |rt	          t          | dd                    st          dt          |           d           t          d           	 t          d          	                                
                                }n# t          t          f$ r d}Y nw xY w|dvrt          d           dS d}|D ]3}|                    |          \  }}	t          d|	            |s|dz  }4t          |          dk    r2t          dt          |          |z
   dt          |                      |rdndS )u  Hand unmanaged skills to the curator by explicit user declaration.

    Provenance cannot be inferred from telemetry: a high patch count proves
    the agent MAINTAINS a skill, not that it AUTHORED it (the agent edits
    user-written skills on the user's behalf constantly). So adoption is never
    automatic — the user names what they're handing over, or passes
    ``--all-unmanaged`` to hand over every eligible skill at once.
    r   r!   r   Nall_unmanagedFz=curator: pass either skill names or --all-unmanaged, not bothr%   z%curator: no unmanaged skills to adoptz7curator: name a skill to adopt, or pass --all-unmanagedr   zcurator: would adopt z skill(s) (dry run):z  + yeszcurator: adopt z, unmanaged skill(s) into curator management?z9  they become eligible for automatic staleness + archivalz  proceed? [y/N] rC   >   yr   curator: abortedr@   zcurator: adopted /)r,   r"   listr   r   r1   list_unmanaged_skill_namesr0   inputstriplowerEOFErrorKeyboardInterruptadopt_skill)
ry   r"   names	adopt_allr   nreplyfailedokr   s
             r   
_cmd_adoptr   X  s    "!!!!!w--344EWT?E::;;I  	QRRR16688 	9:::1 GHHHq74E2233G Fc%jjFFFGGG 	 	A***q  	gdE599:: 	XE

XXXYYYIJJJ	-..4466<<>>EE+, 	 	 	EEE	$$$%%%1F  ))!,,C#    	aKF
5zzA~~D#e**v"5DDE

DDEEE11As   =3E1 1FFc                   ddl m}m} |                    d          }	 |                    | j                  \  }}|                    |           n# |                    |           w xY wt          d|            |rdndS )Nr   skill_ledgerr"   userr@   r%   )r,   r   r"   set_ledger_actorrestore_skillr   reset_ledger_actorr1   ry   r   r"   tokr   r   s         r   _cmd_restorer     s    ////////

'
'
/
/C-++DJ77C'',,,,'',,,,	
c

>11s   A A)c                   ddl m}m} |                    | j                                      d          r"t          d| j         d| j         d           dS |                    d          }	 |                    | j                  \  }}|	                    |           n# |	                    |           w xY wt          d	|            |rdndS )
zManually archive an agent-created skill. Refuses if pinned.

    The auto-curator archives stale skills on its own schedule; this verb is
    for the user who wants to archive *now* without waiting for a run.
    r   r   rM   r   u7   ' is pinned — unpin first with `hermes curator unpin `r%   r   r@   )
r,   r   r"   
get_recordr   r'   r1   r   archive_skillr   r   s         r   _cmd_archiver     s	    0///////dj))--h77 3 3 3%)Z3 3 3	
 	
 	
 q

'
'
/
/C-++DJ77C'',,,,'',,,,	
c

>11s   .B! !B8recorddictOptional[int]c                   |                      d          p|                      d          }|sdS 	 t          j        t          |                    }n# t          t
          f$ r Y dS w xY w|j         |                    t          j	                  }t          dt          j        t          j	                  |z
  j                  S )u  Days since the skill's last activity (view / use / patch).

    Falls back to ``created_at`` so a skill that was authored but never used
    can still be pruned — otherwise never-touched skills would be immortal.
    Returns None only when both fields are missing or unparseable.
    rX   rY   Nr   r   )r'   r   r   r   r   r   r   r   r   r   maxr   days)r   r   r   s      r   
_idle_daysr     s     
&	'	'	C6::l+C+CB t#CGG,,z"   tt	yZZx|Z,,q8<--28999s   !A A'&A'c                8   ddl m} t          | dd          }|dk     r!t          d| dt          j                   d	S t          t          | d
d                    }t          t          | dd                    }g }|                                D ]l}|                    d          r|                    d          |j	        k    r7t          |          }|||k     rO|                    |d         |f           m|st          d| d           dS |                    d            t          dt          |           d| d           |D ]\  }}t          d|dd| d           |rt          d           dS |s	 t          dt          |           d                                                                          }	n'# t"          t$          f$ r t          d           Y dS w xY w|	d vrt          d!           dS d}
g }|D ]<\  }}|                    |          \  }}|r|
dz  }
%|                    ||f           =t          d"|
 d#t          |                      |r.t          d$           |D ]\  }}t          d| d%|            dS dS )&a  Bulk-archive curator-managed skills idle for >= N days.

    Pinned skills are exempt. Already-archived skills are skipped. Default
    ``--days 90`` matches a conservative read of the curator's own archive
    threshold; adjust with ``--days``. Use ``--dry-run`` to preview.
    r   r!   r   Z   r%   z"curator: --days must be >= 1 (got rS   )file   r   Fr   rM   rL   NrN   z6curator: nothing to prune (no unpinned skills idle >= zd)c                    | d          S )Nr%   r   )cs    r   r[   z_cmd_prune.<locals>.<lambda>  s    1Q4% r+   r\   r@   z skill(s) idle >= d:rT   r_   z idle rE   u   
(dry run — no changes made)z	
Archive z skill(s)? [y/N] z
curator: aborted>   r   r   r   z
curator: archived r   z	failures:: )r,   r"   r   r1   sysstderrr   rt   r'   STATE_ARCHIVEDr   rv   sortr0   r   r   r   r   r   r   )ry   r"   r   r   skip_confirm
candidatesr)   idlerN   r   rK   failures_r   r   s                  r   
_cmd_pruner     sG    "!!!!!4$$Daxx:4:::LLLLq74E2233GeU3344LJ'')) - -55?? 	55>>[777!}}<4$;;1V9d+,,,, OtOOOPPPqOOO(((	
Ac*oo
A
A
A
A
ABBB  , ,
d*4***4***++++ /000q 	Is:IIIJJPPRRXXZZEE+, 	 	 	&'''11	 $$$%%%1HH ) )a++D11C 	)MHHOOT3K((((	
=
=
=C
OO
=
=>>> k! 	& 	&ID#$t$$s$$%%%%q1s   AG	 	 G-,G-c                   ddl m} |                                st          d           dS t	          | dd          pd}|                    |          }|t          d	           dS t          d
|j                    dS )zuTake a manual snapshot of the skills tree. Same mechanism as the
    automatic pre-run snapshot, just user-initiated.r   curator_backupzacurator: backups are disabled via config (`curator.backup.enabled: false`); re-enable to snapshotr%   reasonNmanual)r  uE   curator: snapshot failed — check logs (backup disabled or IO error)z?curator: snapshot created at ~/.hermes/skills/.curator_backups/)rP   r  rm   r1   r   snapshot_skillsrN   )ry   r  r  snaps       r   _cmd_backupr    s     %$$$$$$$&& G	
 	
 	
 qT8T**6hF)))88D|UVVVq	
WDI
W
WXXX1r+   c                   ddl m} |                    t          | dd          t          | dd          pd          }|st	          d           dS t	          d	d
dddddddddd           |D ]}|                    d          pi }d}|                    d          rd|d          d}n |                    d          rd|d          }t	          |                    d	d          d
dt          |                    d                    dd|                    dd          dd|                    dd          dd|                    dd           | 
           t	          d           dS )z6List per-mutation audit ledger entries (newest first).r   r   r   Nlimit   )r   r
  z8curator: ledger is empty (or skills.ledger is disabled).idz<14rV   whenz<12actorz<8actionz skillevidencerC   absorbed_intou     → absorbed into 'r   rollback_targetu     → rollback of ?r   z
Roll back a single mutation with `hermes curator rollback <id>`; whole-tree snapshots remain available via `hermes curator rollback --list`.)r,   r   list_entriesr   r1   r'   r   )ry   r   r   r)   r  extras         r   _cmd_ledgerr    s   """"""$$dGT**dGT**0b %  D  HIIIq	T
F
F
F
F
F
FW
F
F
F(
F
F
F
FGGG 
 
55$$*<<(( 	GHH_,EHHHEE\\+,, 	GF2C)DFFEuuT3A , ,gaeeDkk&:&:A , ,uuWc""C, ,()h(<(<C, ,uuWc"",$), ,	
 	
 	
 	

 
	V   1r+   c           
        ddl m}m} ddlm} ddlm} t          | dd          }|&t           | |            ddd	          pd          }|dk    rt          d
           dS  |            }|
                                st          d           dS ddl}ddl}|                                |dz  z
  fd|                                D             }	|	st          d| d           dS t          d| d           t          |	          D ]}
t          d|
j                    t          | dd          rt          d           dS t          | dd          s	 t!          dt#          |	           d                                                                          }n'# t(          t*          f$ r t          d           Y dS w xY w|dvrt          d           dS d}t          |	          D ]}
|                    |
          }	 |                    |
           n1# t0          $ r$}t          d|
j         d|            Y d}~Wd}~ww xY w|                    d|
j        |pg g d d!|i"           |dz  }t          d#| d$           dS )%u  Delete archived skills older than curator.archive_ttl_days.

    Explicit command only — never runs automatically. Respects the ledger:
    each purged skill is captured (before-blobs) and recorded as a 'purge'
    entry, so even a purge is auditable and blob-recoverable.
    r   )cfg_getload_configr	  )_archive_dirr   Nr8   archive_ttl_days)defaultzcurator: purge disabled (curator.archive_ttl_days is 0). Set the config key or pass --days N to purge archives older than N days.r%   u3   curator: no archive directory — nothing to purge.r   c                v    g | ]5}|                                 r|                                j        k     3|6S r   )is_dirstatst_mtime)r(   pcutoffs     r   
<listcomp>z_cmd_purge.<locals>.<listcomp>X  sI       88::&&((+f44 	
444r+   z'curator: no archived skills older than zd.zArchived skills older than r   rT   r   Fu   (dry run — nothing deleted)r   zPermanently delete z archived skill(s)? [y/N] 

cancelled>   r   r   	cancelledzcurator: failed to purge r   purger   ttl_days)beforeafterr  r  zcurator: purged z, archived skill(s). Ledger entries recorded.)hermes_cli.configr  r  r,   r   tools.skill_usager  r   r   r1   ro   shutiltimeiterdirrx   rN   r   r0   r   r   r   r   capture_beforermtreeOSErrorappend_entry)ry   r  r  r   r  r'  archive_rootr,  r-  r   r!  anspurgedr(  er"  s                  @r   
_cmd_purger7  :  s}    76666666""""""......tVT**Hww{{}}i9KUVWWW\[\]]1}}O	
 	
 	
 q<>>L   CDDDqMMMKKKYY[[8e++F   ''))  J  DDDDEEEq	
4
4
4
4555J  m16mmtY&& -...q4&& 	Yc*ooYYYZZ``bbhhjjCC+, 	 	 	-   11	 l""+1FJ  ,,Q//	MM! 	 	 	;af;;;;<<<HHHH	 	!!F<R (+ 	" 	
 	
 	
 	!	
QV
Q
Q
QRRR1s+   AF# # GGH
I(IIc                	   ddl m} t          | dd          }|rddlm} |                    |          }|t          d| d           dS t          d	|            t          d
|                    dd                      t          d|                    dd                      t          d|                    dd                      t          d|                    dd                      d |                    d          pg |                    d          pg z   D             }t          dt          |                      t          | dd          sq	 t          d          
                                                                }n'# t          t          f$ r t          d           Y dS w xY w|dvrt          d           dS |                    |          \  }}|rt          d|            dS t          d|            dS t          | dd          r#t          |                                           dS t          | d d          }	|                    |	          }
|
|                                }|st          d!           nWt          d"|	rd#t%          |	          z   nd$ d%           t          d&           t          |                                           dS |                    |
          }t          d'|
j                    |rt          d(|                    d)d                      t          d*|                    d+d                      t          d,|                    d-d                      |                    d.          pi }t+          |t,                    rf|                    d/          r(t          d0|                    d1d           d2           n)|                    d)d3          }t          d4| d5           t          d6           t          | dd          sq	 t          d7          
                                                                }n'# t          t          f$ r t          d           Y dS w xY w|dvrt          d           dS |                    |
j        8          \  }}}|rt          d|            dS t          d|            dS )9u  Restore the skills tree from a snapshot, or a single mutation from
    the audit ledger.

    With a positional ``entry_id``, restores exactly the files touched by
    that one ledger entry (from content-addressed blobs), taking a
    pre-rollback safety ledger entry first — and failing closed when that
    safety capture fails. Without it, behaves as before: whole-tree tarball
    restore. ``--list`` prints available snapshots and exits. ``--id
    <stamp>`` picks a specific snapshot. Without ``-y``, prompts for
    confirmation. A safety snapshot of the current tree is always taken
    first, so rollbacks are themselves undoable.
    r   r  entry_idNr	  zcurator: no ledger entry 'zh'. See `hermes curator ledger` for entry ids, or use `--id <snapshot>` for whole-tree snapshot rollback.r%   zRollback target: ledger entry z
  action: r  r  z
  skill:  r   z
  actor:  r  z
  when:   r   c                8    h | ]}|                     d           S )pathr&   )r(   is     r   	<setcomp>z _cmd_rollback.<locals>.<setcomp>  s"    cccQ155==cccr+   r(  r)  z
  files:  r   Fz,Restore this mutation's before-state? [y/N] r$  >   r   r   r%  r@   u   curator: rollback failed — r   	backup_idzhcurator: no snapshots exist yet. Take one with `hermes curator backup` or wait for the next curator run.zcurator: no snapshot matching zid z
your query.z
Available:zRollback target: z  reason:      r  z  created_at:  rY   z  skill files: skill_files	cron_jobs	backed_upz  cron jobs:   
jobs_countz. (will be restored for skill-link fields only)znot capturedz   cron jobs:   not in snapshot (rS   a  
This will replace the current ~/.hermes/skills/ tree (a safety snapshot of the current state is taken first so this is undoable). Cron jobs that still exist will have their skills/skill fields restored from the snapshot; all other cron fields are left alone.zProceed? [y/N] )r>  )rP   r  r   r,   r   	get_entryr1   r'   r0   r   r   r   r   r   rollback_entrysummarize_backups_resolve_backuplist_backupsrepr_read_manifestrN   
isinstancer   rollback)ry   r  r9  r   entrytouchedr4  r   r   r>  target_pathr   manifestcronr  r   s                   r   _cmd_rollbackrR    s    %$$$$$tZ..H  &&&&&&&&x00=FX F F F  
 19x99:::5599Xs33556664599Wc22445554599Wc22445551599T3//11222cc599X+>+>+D"SZI[I[Ia_a*bccc)3w<<))***tUE** 	JKKQQSSYY[[/0   m$$$qq ,&&k"""q--h77C 	#c##$$$13c33444qtVU## n..00111qk400I 00;;K**,, 	6L   
 M.7I54	??**\M M M   ,.2244555q,,[99H	
0k.
0
0111 D=Xs ; ;==>>>A\3 ? ?AABBBB]C @ @BBCCC||K((.BdD!! 	Dxx$$ DEdhh|Q&?&? E E E   
 (N;;BBBBCCC		L   4&& 	)**002288::CC+, 	 	 	-   11	 l""+1((;3C(DDJBQ	 #   q	
/#
/
/0001s$   !3F  F98F93Q6 6 RRc                    ddl m} |                                }|st          d           dS |D ]}t          |           dS )z#List archived (recoverable) skills.r   r!   zcurator: no archived skills)r,   r"   list_archived_skill_namesr1   )ry   r"   r   rN   s       r   _cmd_list_archivedrU    sb    !!!!!!1133E +,,,q  d1r+   c                   ddl }ddlm} |                                }t	          | dd          rfd|D             }t	          | dd          }|dk    r|                    d	 
           n7|dk    r|                    d d           n|                    d d           t	          | dd          r't          |                    |dd                     dS |st          d           dS dddd}|D ]F}|                    |                    dd          d          dz   ||                    dd          <   Gt          dt          |           d|d          d|d          d|d          d	           t                       t          dd d!dd"d#dd$d%dd&d%dd'd(dd)d%d*           |D ]}t          |                    d+                    }t          d|d         dd,         d!d|                    dd          d#d|                    d-d          d.d|                    d/d          d.d|                    d0d          d1d|                    d2d          d.d|            dS )3u  Show usage telemetry for ALL skills, with provenance.

    Unlike `status` (curator-scoped to curated candidates), this lists
    every skill on disk — bundled built-ins and hub-installed included — so you
    can see how often each is actually used regardless of curation.
    r   Nr!   rO   c                F    g | ]}|                     d           k    |S )rO   r&   )r(   r)   prov_filters     r   r#  z_cmd_usage.<locals>.<listcomp>  s/    FFFa155#6#6+#E#E#E#E#Er+   r   activityrN   c                    | d         S r   r   rZ   s    r   r[   z_cmd_usage.<locals>.<lambda>  s
    &	 r+   r\   recentc                0    |                      d          pdS )NrX   rC   r&   rZ   s    r   r[   z_cmd_usage.<locals>.<lambda>  s    &8 9 9 ?R r+   Tri   c                .    |                      dd          S )Nr`   r   r&   rZ   s    r   r[   z_cmd_usage.<locals>.<lambda>  s    &6 : : r+   jsonFr   )indentensure_asciizcurator: no skills foundrP   rQ   hubrP   r%   zskills: z total  (agent=rR   rQ   z  hub=rb  rS   rT   r   r_   origin8susez>4sviewpatchz>5sactz  last_activityrX   (   rb   z>4drc   rd   z>5dr`   )r^  r,   r"   usage_reportr   r   r1   dumpsr'   r0   r   )	ry   _jsonr"   r   sort_keycountsr)   r   rX  s	           @r   
_cmd_usagero    s    !!!!!!##%%D$d33K GFFFF4FFFtVZ00H6		))	****	X				??	NNNN		::D	IIItVU## ekk$quk==>>>q ()))q Qq11F _ _/5zz!%%g:V:VXY/Z/Z]^/^quu\7++,,		W3t99 	W 	W/	W 	W-3I->	W 	WFLUm	W 	W 	W   
GGG		PW+ 	P 	PH+ 	P 	PO	P 	PO	P 	P&-O	P 	P5:O	P 	P 	P    

 

quu/001163B3'  uu\7++2 uu[!$$,  uu\1%%-  uu]A&&.	 
 uu%q))1   	
 	
 	
 	
 1r+   parentargparse.ArgumentParserc                z                           fd                                d          }|                    dd          }|                     t                     |                    dd	          }|                    d
ddd           |                    dddd           |                    ddd           |                     t
                     |                    dd          }|                    ddddd           |                    dddd            |                    d!d"dd#           |                    d$d%dd&           |                     t                     |                    d'd(          }|                     t                     |                    d)d*          }|                     t                     |                    d+d,          }|                    d-d.           |                     t                     |                    d/d0          }|                    d-d.           |                     t                     |                    d1d2                               t                     |                    d3d4          }	|	                    d-d5d67           |	                    d8dd9           |	                    d!dd:           |	                    d;dd<           |	                     t                     |                    d=d>          }
|
                    d-d.           |
                     t                     |                    d?d@                               t                     |                    dAdB          }|                    d-d.           |                     t                     |                    dCdD          }|                    dEt           dFdGH           |                    dId;ddJ           |                    d!d"ddK           |                     t"                     |                    dLdM          }|                    dNddOP           |                     t$                     |                    dQdR          }|                    dSdTddUV           |                    dWddX           |                    dYdZdd[\           |                    dId;dd]           |                     t&                     |                    d^d_          }|                    d`ddaP           |                    dbt           dcddH           |                     t(                     |                    dedf          }|                    dEt           ddgH           |                    d!d"ddh           |                    dId;ddJ           |                     t*                     dS )izAttach `curator` subcommands to *parent*.

    main.py calls this with the ArgumentParser returned by
    ``subparsers.add_parser("curator", ...)``.
    c                <                                     dfd         S )Nr   r%   )
print_help)arp  s    r   r[   zregister_cli.<locals>.<lambda>H  s    (9(9(;(;Q'?'B r+   )funccurator_command)deststatusz#Show curator status and skill stats)helpusagezJShow usage telemetry for ALL skills (built-in, hub, agent) with provenancez--sort)rY  r[  rN   rY  zlSort order: activity (most-used first, default), recent (most-recently-active first), or name (alphabetical))choicesr  rz  z--provenancera  NzOnly show skills of this originz--json
store_truez/Emit the full report as JSON instead of a table)r  rz  runzTrigger a curator review nowz--syncz--synchronousr   z@Wait for the LLM review pass to finish (default for manual runs))rx  r  rz  z--backgroundr   zGStart the LLM review pass in a background thread and return immediatelyz	--dry-runr   uk   Report only — no state changes, no archives, no consolidation (use this to preview what curator would do)z--consolidater   zForce the LLM umbrella-building consolidation pass on for this run, overriding the config default (off). Without this flag the run is prune-only unless `curator.consolidate: true` is set.pausezPause the curator until resumedresumezResume a paused curatorpinz4Pin a skill so the curator never auto-transitions itr   z
Skill nameunpinzUnpin a skillzlist-unmanagedz7List curation-eligible skills with no provenance markeradoptzGHand unmanaged skills to the curator (provenance is a user declaration)*z8Skill name(s) to adopt. Omit when using --all-unmanaged.)nargsrz  z--all-unmanagedzAAdopt every curation-eligible skill that has no provenance markerz3List what would be adopted without writing anythingz--yesz0Skip the confirmation prompt for --all-unmanagedrestorezRestore an archived skillzlist-archivedzList archived skillsarchivezBManually archive a skill (move to .archive/, excluded from prompt)prunezCBulk-archive curator-managed skills idle for >= N days (default 90)z--daysr   z5Archive skills idle for at least N days (default: 90))typer  rz  z-yzSkip the confirmation promptz,Show what would be archived without doing itbackupzoTake a manual tar.gz snapshot of ~/.hermes/skills/ (curator also does this automatically before every real run)z--reasonz;Free-text label stored in manifest.json (default: 'manual'))r  rz  rL  zxRestore ~/.hermes/skills/ from a curator snapshot, or a single mutation by ledger entry id (see `hermes curator ledger`)r9  r  zsLedger entry id for single-mutation rollback (from `hermes curator ledger`). Omit for whole-tree snapshot rollback.)r  r  rz  z--listz3List available snapshots and exit without restoringz--idr>  z6Snapshot id to restore (see `--list`); default: newest)rx  r  rz  zSkip confirmation promptledgerzIList the per-mutation skill audit ledger (all actors: curator/agent/user)z--skillz Only show entries for this skillz--limitr  z!Max entries to show (default: 20)r&  uv   Delete archived skills older than curator.archive_ttl_days (explicit only — never automatic; recorded in the ledger)z5Override curator.archive_ttl_days for this invocationz*Show what would be purged without deleting)set_defaultsadd_subparsers
add_parserr   add_argumentro  r   r   r   r   r   r   r   r   rU  r   r   r   r  rR  r  r7  )rp  subsp_statusp_usagep_runp_pausep_resumep_pinp_unpinp_adopt	p_restore	p_archivep_prunep_backup
p_rollbackp_ledgerp_purges   `                r   register_clir  B  s    BBBBCCC  &7 88Dx.STTH{+++ooY   G 8*D    
  ;T.     >     j)))OOE(FOGGE	/lO     
\,V     
)L;    
 
mLL     
H%%%oog,MoNNGj)))x.GHHH{+++OOE(^O__E	w\222	H%%%oogOo<<G|444j)))OOF    l+l,,,ooV   G sG     ,P     LB     ?     j)))	0KLLI7666---OOO*@OAA	-	.	.	.Q    I 7666---ooR   G sBD     gl+     )L;     j)))L   H
 DJ     {+++I !  J
 #tP    
 B     [$E     gl'     ///#   H
 4/     R0     {+++ooK   G
 sDD     )L9     gl+     j)))))r+   c                    t          j        d          }t          |           |                    |           }t	          |dd          }||                                 dS t           ||          pd          S )z>Standalone entry (also usable by hermes_cli.main fallthrough).zhermes curator)progrv  Nr   )argparseArgumentParserr  
parse_argsr   rt  r   )argvparserry   fns       r   cli_mainr    s~    $*:;;;FT""D	vt	$	$B	zqrr$xx}1r+   __main__)r   r	   r
   r   )r
   r   )r
   r   )r   r   r
   r   )rp  rq  r
   r   r   )#__doc__
__future__r   r  r   r   r   pathlibr   typingr   r   r5   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r7  rR  rU  ro  r  r  __name__exitr   r+   r   <module>r     s}    # " " " " "  



 ' ' ' ' ' ' ' '            # # # #(   :N N N Nb< < < <~      
 
 
 

 
 
 
   85 5 5 5p      ,: : : :&A A A AH   &   >H H H HVn n n nb	 	 	 	9 9 9 9@A* A* A* A*H	 	 	 	 	 zCHXXZZ r+   