
    iO                       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$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 agoiQ zh agozd ago)r   fromisoformat	TypeError
ValueErrorr   r   replacer   utcnowinttotal_seconds)r   dtdeltasecss       7/home/piyush/.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     >>r   c                z   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          |                      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           |                                }|st          d           dS g g g d}g }|D ]q}|                    dd          }|                    |g                               |           |                    d           r|                    |d!                    rt          d"t#          |           d#           dD ];}|                    |g           }t          d$|d%d&t#          |                      <|r5t          d't#          |           d(d)                    |                      t'          |                    dg           d* +          d d,         }|rt          d-           |D ]}t          |                    d.                    }t          d$|d!         d/d0|                    d1d          d2d3|                    d4d          d2d5|                    d6d          d2d7|                    d8d          d2d9|            |                    dg           }|rt'          |d: d;<          d d,         }|r|d                             d1          pddk    rt          d=           |D ]}t          |                    d.                    }t          d$|d!         d/d0|                    d1d          d2d3|                    d4d          d2d5|                    d6d          d2d7|                    d8d          d2d9|            t'          |d> +          d d,         }|rt          d?           |D ]}t          |                    d.                    }t          d$|d!         d/d0|                    d1d          d2d3|                    d4d          d2d5|                    d6d          d2d7|                    d8d          d2d9|            dS )@Nr   curatorskill_usagepausedFlast_run_atlast_run_summaryz(none)	run_countENABLEDPAUSEDDISABLED	curator: z  runs:           z  last run:       z  last summary:   last_report_path z
 (missing)z  last report:       dhz  interval:       every z  stale after:    zd unusedz  archive after:  z
no agent-created skills)activestalearchivedstater0   pinnednamez
agent-created skills: z total  10s z	
pinned (z): z, c                Z    |                      d          p|                      d          pdS )Nlast_activity_at
created_atr,   getrs    r   <lambda>z_cmd_status.<locals>.<lambda>b   s)    aee.//L1553F3FL"     key   z
least recently active (top 5):r:   40sz  activity=activity_count3dz  use=	use_countz  view=
view_countz
  patches=patch_countz  last_activity=c                ^    |                      d          pd|                      d          pdfS NrF   r   r:   r,   r<   r>   s    r   r@   z_cmd_status.<locals>.<lambda>{   0    155!1227a?Q9R9R9XVXY rA   T)rC   reversez
most active (top 5):c                ^    |                      d          pd|                      d          pdfS rL   r<   r>   s    r   r@   z_cmd_status.<locals>.<lambda>   rM   rA   z
least active (top 5):)agentr    toolsr"   
load_state
is_enabledr=   printr   r   existsget_interval_hoursget_stale_after_daysget_archive_after_daysagent_created_report
setdefaultappendlenjoinsorted)argsr    r"   r3   enabledr#   last_runsummaryrunsstatus_line_reportsuffix_ih_interval_labelrowsby_stater4   r?   
state_namebucketr0   last
active_allmost_activeleast_actives                            r   _cmd_statusrq   '   sL   !!!!!!  E  ""GYYx''Fyy''Hii*++7xG99[!$$D  	V 			 	 
 

#k
#
#$$$	
%t
%
%&&&	
2wx00
2
2333	
(w
(
()))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++--D )***qrr::HF % %UU7H--
J++22155555?? 	%MM!F)$$$	
6SYY
6
6
6777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=	, ,
 &*, ,    1rA   c                R   ddl m} |                                st          d           dS t	          t          | dd                    }t	          t          | dd                    }t	          t          | dd                    p| }|rt          d	           n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   r   zAcurator: disabled via config; enable with `curator.enabled: true`   dry_runF
backgroundsynchronousz7curator: running DRY-RUN (report only, no mutations)...zcurator: running review pass...msgr   r
   Nonec                $    t          |            d S N)rT   )rw   s    r   _on_summaryz_cmd_run.<locals>._on_summary   s    c




rA   )
on_summaryrv   rt   auto_transitionszauto (preview): checkedu9    candidate skill(s) — no transitions applied in dry-runzauto: checked=z stale=marked_stalez
 archived=r2   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.)rw   r   r
   rx   )rP   r    rS   rT   boolgetattrrun_curator_reviewr=   )r_   r    dryru   rv   r{   resultautos           r   _cmd_runr      s?    QRRRq
wtY..
/
/CgdL%8899Jwt]E::;;M:~K
 1GHHHH/000    '' (  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
 
 		[   
 [   1rA   c                Z    ddl m} |                    d           t          d           dS )Nr   r   Tzcurator: pausedrP   r    
set_pausedrT   r_   r    s     r   
_cmd_pauser      s<    t	
1rA   c                Z    ddl m} |                    d           t          d           dS )Nr   r   Fzcurator: resumedr   r   s     r   _cmd_resumer      s<    u	
1rA   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)rs   Tzcurator: pinned 'z ' (will bypass auto-transitions)rQ   r"   is_agent_createdskillrT   
set_pinnedr_   r"   s     r   _cmd_pinr      s    !!!!!!''
33 B B B B	
 	
 	
 q4:t,,,	
Jdj
J
J
JKKK1rA   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)rs   Fzcurator: unpinned ''r   r   s     r   
_cmd_unpinr      s    !!!!!!''
33 R R R R	
 	
 	
 q4:u---	
-

-
-
-...1rA   c                x    ddl m} |                    | j                  \  }}t	          d|            |rdndS )Nr   r!   r*   rs   )rQ   r"   restore_skillr   rT   r_   r"   okrw   s       r   _cmd_restorer      sR    !!!!!!''
33GB	
c

>11rA   c                   ddl m} |                    | j                                      d          r"t          d| j         d| j         d           dS |                    | j                  \  }}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!   r4   r   u7   ' is pinned — unpin first with `hermes curator unpin `rs   r*   )rQ   r"   
get_recordr   r=   rT   archive_skillr   s       r   _cmd_archiver     s     "!!!!!dj))--h77 3 3 3%)Z3 3 3	
 	
 	
 q''
33GB	
c

>11rA   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.
    r:   r;   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 agent-created 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   rs   z"curator: --days must be >= 1 (got ))file   rt   Fyesr4   r3   Nr5   z6curator: nothing to prune (no unpinned skills idle >= zd)c                    | d          S )Nrs    )cs    r   r@   z_cmd_prune.<locals>.<lambda>F  s    1Q4% rA   rB   r*   z skill(s) idle >= zd:r6   rE   z idle r.   u   
(dry run — no changes made)z	
Archive z skill(s)? [y/N] z
curator: abortedyr   zcurator: abortedz
curator: archived /z	failures:z: )rQ   r"   r   rT   sysstderrr   rY   r=   STATE_ARCHIVEDr   r[   sortr\   inputstriplowerEOFErrorKeyboardInterruptr   )r_   r"   r   rt   skip_confirm
candidatesr?   idler5   replyr2   failures_r   rw   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 snapshotrs   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   rS   rT   r   snapshot_skillsr5   )r_   r   r   snaps       r   _cmd_backupr   k  s     %$$$$$$$&& G	
 	
 	
 qT8T**6hF)))88D|UVVVq	
WDI
W
WXXX1rA   c                   ddl m} 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          d|
                    dd           d           n)|
                    dd          }t          d| d           t          d           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 |                    |j	        $          \  }	}
}|	rt          d%|
            dS t          d&|
            dS )'a7  Restore the skills tree from a snapshot. Defaults to newest.

    ``--list`` prints available snapshots and exits. ``--id <stamp>`` picks
    a specific one. Without ``-y``, prompts for confirmation. A safety
    snapshot of the current tree is always taken first, so rollbacks are
    themselves undoable.
    r   r   listF	backup_idNzhcurator: 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:rs   zRollback target: z  reason:      r   ?z  created_at:  r;   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 (r   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.r   zProceed? [y/N] z

cancelledr   	cancelled)r   r*   u   curator: rollback failed — )rP   r   r   rT   summarize_backups_resolve_backuplist_backupsrepr_read_manifestr5   r=   
isinstancer   r   r   r   r   r   rollback)r_   r   r   target_pathri   manifestcronr   ansr   rw   r   s               r   _cmd_rollbackr   ~  s}    %$$$$$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   63I* * JJ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)rQ   r"   list_archived_skill_namesrT   )r_   r"   namesr5   s       r   _cmd_list_archivedr     sb    !!!!!!1133E +,,,q  d1rA   parentargparse.ArgumentParserrx   c                                           fd                                d          }|                    dd          }|                     t                     |                    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d          }|                    dd           |                     t                     |                    d d!          }|                    dd           |                     t                     |                    d"d#                               t                     |                    d$d%          }	|	                    dd           |	                     t                     |                    d&d'          }
|
                    d(t          d)d*+           |
                    d,d-dd./           |
                    dddd0           |
                     t                     |                    d1d2          }|                    d3d4d56           |                     t                     |                    d7d8          }|                    d9dd:/           |                    d;d<d4d=>           |                    d,d-dd?/           |                     t                      d4S )@zAttach `curator` subcommands to *parent*.

    main.py calls this with the ArgumentParser returned by
    ``subparsers.add_parser("curator", ...)``.
    c                <                                     dfd         S )Nr   rs   )
print_help)ar   s    r   r@   zregister_cli.<locals>.<lambda>  s    (9(9(;(;Q'?'B rA   )funccurator_command)deststatusz#Show curator status and skill stats)helprunzTrigger a curator review nowz--syncz--synchronousrv   
store_truez@Wait for the LLM review pass to finish (default for manual runs))r   actionr   z--backgroundru   zGStart the LLM review pass in a background thread and return immediatelyz	--dry-runrt   uk   Report only — no state changes, no archives, no consolidation (use this to preview what curator would do)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 skillrestorezRestore an archived skillzlist-archivedzList archived skillsarchivezBManually archive a skill (move to .archive/, excluded from prompt)prunezABulk-archive agent-created skills idle for >= N days (default 90)z--daysr   z5Archive skills idle for at least N days (default: 90))typedefaultr   z-yz--yeszSkip the confirmation prompt)r   r   z,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--reasonNz;Free-text label stored in manifest.json (default: 'manual'))r   r   r   zJRestore ~/.hermes/skills/ from a curator snapshot (defaults to the newest)z--listz3List available snapshots and exit without restoringz--idr   z6Snapshot id to restore (see `--list`); default: newest)r   r   r   zSkip confirmation prompt)set_defaultsadd_subparsers
add_parserrq   add_argumentr   r   r   r   r   r   r   r   r   r   r   r   )r   subsp_statusp_runp_pausep_resumep_pinp_unpin	p_restore	p_archivep_prunep_backup
p_rollbacks   `            r   register_clir    s    BBBBCCC  &7 88Dx.STTH{+++OOE(FOGGE	/lO     
\,V     
)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)))	0KLLI7666---OOO*@OAA	-	.	.	.Q    I 7666---ooP   G sBD     gl+     )L;     j)))L   H
 DJ     {+++( !  J
 B     [$E     gl'     /////rA   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)progr   Nr   )argparseArgumentParserr  
parse_argsr   r   r   )argvparserr_   fns       r   cli_mainr  @  s~    $*:;;;FT""D	vt	$	$B	zqrr$xx}1rA   __main__)r   r	   r
   r   )r
   r   )r   r   r
   r   )r   r   r
   rx   rz   )__doc__
__future__r   r  r   r   r   pathlibr   typingr   r   rq   r   r   r   r   r   r   r   r   r   r   r   r   r  r  __name__exitr   rA   r   <module>r"     s    # " " " " "  



 ' ' ' ' ' ' ' '            # # # #(u u u up1 1 1 1h      
 
 
 

 
 
 
      $: : : :&A A A AH   &F F F FR	 	 	 	 f0 f0 f0 f0R	 	 	 	 	 zCHXXZZ rA   