
    iH                        d 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Zddlm	Z	 ddl
mZmZmZ ddlmZmZ  ej        d          Zg dZg dZd	d
gZg dZdefdZ eh d          Z eh d          Z eh d          ZdefdZdefdZdefdZdefdZde ddfdZ!de defdZ"de de#fdZ$de dee          fdZ%de#fdZ&de dee         fdZ'de de#fdZ(e	 G d d                      Z)d ede*fd!Z+d ede#fd"Z,d ede-fd#Z.dee)         fd$Z/	 	 	 	 dIde d&ee          d'e#d(e#d)e#defd*Z0dJd ed+e#dee1         fd,Z2dJde d-e#defd.Z3de d eddfd/Z4d eddfd0Z5de fd1Z6de ddfd2Z7de fd3Z8d4efd5Z9de d6e defd7Z:d8e dee          fd9Z;d:ed;eddfd<Z<d:ede=e          fd=Z>dKd>e dee          defd?Z?d@e dAe dBeddfdCZ@d@e dAe defdDZAde fdEZBde fdFZCdGe de fdHZDdS )Lu_  
Profile management for multiple isolated Hermes instances.

Each profile is a fully independent HERMES_HOME directory with its own
config.yaml, .env, memory, sessions, skills, gateway, cron, and logs.
Profiles live under ``~/.hermes/profiles/<name>/`` by default.

The "default" profile is ``~/.hermes`` itself — backward compatible,
zero migration needed.

Usage::

    hermes profile create coder          # fresh profile + bundled skills
    hermes profile create coder --clone  # also copy config, .env, SOUL.md, skills
    hermes profile create coder --clone-all  # full copy of source profile
    coder chat                           # use via wrapper alias
    hermes -p coder chat                 # or via flag
    hermes profile use coder             # set as sticky default
    hermes profile delete coder          # remove profile + alias + service
    N)	dataclass)PathPurePosixPathPureWindowsPath)ListOptionalz^[a-z0-9][a-z0-9_-]{0,63}$)	memoriessessionsskillsskinslogsplans	workspacecronhome)config.yaml.envSOUL.mdzmemories/MEMORY.mdzmemories/USER.md)gateway.pidgateway_state.jsonprocesses.json
source_dirc                     |                                  dt          dt          t                   dt          t                   ffd}|S )u  Ignore ``profiles/`` at the root of *source_dir* only.

    ``~/.hermes`` contains ``profiles/<name>/`` for sibling named profiles.
    ``shutil.copytree`` would otherwise duplicate that entire tree inside the
    new profile (recursive ``.../profiles/.../profiles/...``). Export already
    excludes ``profiles`` via ``_DEFAULT_EXPORT_EXCLUDE_ROOT`` — match that
    behavior for ``--clone-all``.
    	directorynamesreturnc                     	 t          |                                           k    rd |D             S n# t          t          f$ r Y nw xY wg S )Nc                     g | ]
}|d k    |S )profiles ).0ns     8/home/ubuntu/.hermes/hermes-agent/hermes_cli/profiles.py
<listcomp>z?_clone_all_copytree_ignore.<locals>._ignore.<locals>.<listcomp>Y   s    <<<aAOOOOO    )r   resolveOSError
ValueError)r   r   source_resolveds     r#   _ignorez+_clone_all_copytree_ignore.<locals>._ignoreV   sh    	I&&((O;;<<5<<<< <$ 	 	 	D		s   05 A	A	)r&   strr   )r   r*   r)   s     @r#   _clone_all_copytree_ignorer,   K   sX     !((**O3 tCy T#Y       Nr%   >   state.db	auth.lock
.worktrees
errors.loghermes-agentstate.db-shmstate.db-wal.update_check.hermes_historyhermes_state.dbresponse_store.dbresponse_store.db-shmresponse_store.db-walbinr   r   	sandboxesaudio_cachecheckpointsimage_cachenode_modulesactive_profiledocument_cachebrowser_screenshotsr   	auth.jsonr   r   r   >   tmprootsudotesthermesdefault>   acpmcpchatr   dumploginmodelsetuptoolsconfigdoctorhonchologoutr   statusupdategatewaypairingpluginsprofileversioninsightsr
   whatsapp	uninstallr   c                  $    t                      dz  S )a  Return the directory where named profiles are stored.

    Anchored to the hermes root, NOT to the current HERMES_HOME
    (which may itself be a profile).  This ensures ``coder profile list``
    can see all profiles.

    In Docker/custom deployments where HERMES_HOME points outside
    ``~/.hermes``, profiles live under ``HERMES_HOME/profiles/`` so
    they persist on the mounted volume.
    r   _get_default_hermes_homer    r%   r#   _get_profiles_rootrc      s     $%%
22r%   c                  "    ddl m}   |             S )zReturn the default (pre-profile) HERMES_HOME path.

    In standard deployments this is ``~/.hermes``.
    In Docker/custom deployments where HERMES_HOME is outside ``~/.hermes``
    (e.g. ``/opt/data``), returns HERMES_HOME directly.
    r   get_default_hermes_root)hermes_constantsrf   re   s    r#   rb   rb      s%     988888""$$$r%   c                  $    t                      dz  S )z2Return the path to the sticky active_profile file.r@   ra   r    r%   r#   _get_active_profile_pathri      s    #%%(888r%   c                  4    t          j                    dz  dz  S )z)Return the directory for wrapper scripts.z.localr:   )r   r   r    r%   r#   _get_wrapper_dirrk      s    9;;!E))r%   namec                 p    | dk    rdS t                               |           st          d| d          dS )zARaise ``ValueError`` if *name* is not a valid profile identifier.rI   NzInvalid profile name z%. Must match [a-z0-9][a-z0-9_-]{0,63})_PROFILE_ID_REmatchr(   rl   s    r#   validate_profile_namerq      sZ    y%% 
*D * * *
 
 	

 
r%   c                 L    | dk    rt                      S t                      | z  S )z4Resolve a profile name to its HERMES_HOME directory.rI   )rb   rc   rp   s    r#   get_profile_dirrs      s*    y')))$&&r%   c                 T    | dk    rdS t          |                                           S )z)Check whether a profile directory exists.rI   T)rs   is_dirrp   s    r#   profile_existsrv      s,    yt4  '')))r%   c                    | t           v rd|  dS | t          v rd|  dS t                      }	 t          j        d| gddd          }|j        dk    rg|j                                        }|t          || z            k    r/	 || z  	                                }d	|v rd
S n# t          $ r Y nw xY wd|  d| dS n# t          t          j        f$ r Y nw xY wd
S )zReturn a human-readable collision message, or None if the name is safe.

    Checks: reserved names, hermes subcommands, existing binaries in PATH.
    'z' is a reserved namez$' conflicts with a hermes subcommandwhichT   )capture_outputtexttimeoutr   	hermes -pNz&' conflicts with an existing command ())_RESERVED_NAMES_HERMES_SUBCOMMANDSrk   
subprocessrun
returncodestdoutstripr+   	read_text	ExceptionFileNotFoundErrorTimeoutExpired)rl   wrapper_dirresultexisting_pathcontents        r#   check_alias_collisionr      sL   
 -4----"""=4==== #$$KdODtQ
 
 
 !!"M//11MK$$6 7 777*T1<<>>G"g--#t .    DStSS=SSSS " z89    4s6   AB; B!  B; !
B.+B; -B..B; ;CCc                      t          t                                } | t          j                            dd                              t          j                  v S )z!Check if ~/.local/bin is in PATH.PATH )r+   rk   osenvirongetsplitpathsep)r   s    r#   _is_wrapper_dir_in_pathr      sA    &(())K"*..44::2:FFFFr%   c                    t                      }	 |                    dd           n-# t          $ r }t          d| d|            Y d}~dS d}~ww xY w|| z  }	 |                    d|  d           |                    |                                j        t          j        z  t          j	        z  t          j
        z             |S # t          $ r }t          d| d|            Y d}~dS d}~ww xY w)	zCreate a shell wrapper script at ~/.local/bin/<name>.

    Returns the path to the created wrapper, or None if creation failed.
    Tparentsexist_oku   ⚠ Could not create : Nz#!/bin/sh
exec hermes -p z "$@"
u    ⚠ Could not create wrapper at )rk   mkdirr'   print
write_textchmodstatst_modeS_IEXECS_IXGRPS_IXOTH)rl   r   ewrapper_paths       r#   create_wrapper_scriptr      s7   
 #$$K$6666   8k88Q88999ttttt %L JT J J JKKK<,,..6ETW[Wccddd   DDDDDEEEttttts-   ( 
AAAA-C	 	
C3C..C3c                     t                      | z  }|                                r@	 |                                }d|v r|                                 dS n# t          $ r Y nw xY wdS )zARemove the wrapper script for a profile. Returns True if removed.r~   TF)rk   existsr   unlinkr   )rl   r   r   s      r#   remove_wrapper_scriptr     s    #%%,L 	",,..Gg%%##%%%t &  	 	 	D	5s   ,A 
A#"A#c                       e Zd ZU dZeed<   eed<   eed<   eed<   dZe	e         ed<   dZ
e	e         ed<   d	Zeed
<   dZeed<   dZe	e         ed<   dS )ProfileInfoz$Summary information about a profile.rl   path
is_defaultgateway_runningNrO   providerFhas_envr   skill_count
alias_path)__name__
__module____qualname____doc__r+   __annotations__r   boolrO   r   r   r   r   intr   r    r%   r#   r   r   #  s         ..
III
JJJE8C="Hhsm"""GTK!%J%%%%%r%   r   profile_dirc                    | dz  }|                                 sdS 	 ddl}t          |d          5 }|                    |          pi }ddd           n# 1 swxY w Y   |                    di           }t          |t                    r|dfS t          |t                    r?|                    d          p|                    d          |                    d          fS dS # t          $ r Y dS w xY w)	zLRead model/provider from a profile's config.yaml. Returns (model, provider).r   )NNr   NrrO   rI   r   )	r   yamlopen	safe_loadr   
isinstancer+   dictr   )r   config_pathr   fcfg	model_cfgs         r#   _read_config_modelr   1  sI   -K z+s## 	*q..##)rC	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	*GGGR((	i%% 	#d?"i&& 	a==++Ey}}W/E/Ey}}U_G`G```z   zzs;   C% A	C% AC% A1C% AC% %
C32C3c                 V    	 ddl m}  || dz  d          duS # t          $ r Y dS w xY w)z<Check if a gateway is running for a given profile directory.r   )get_running_pidr   F)cleanup_staleN)gateway.statusr   r   )r   r   s     r#   _check_gateway_runningr   D  s[    222222{]:%PPPX\\\   uus    
((c                     | dz  }|                                 sdS d}|                    d          D ])}dt          |          vrdt          |          vr|dz  }*|S )z$Count installed skills in a profile.r   r   zSKILL.mdz/.hub/z/.git/   )ru   rglobr+   )r   
skills_dircountmds       r#   _count_skillsr   M  sw    x'J qEz**  3r77""xs2ww'>'>QJELr%   c                  H   g } t                      }t                      }|                                rkt          |          \  }}|                     t          d|dt          |          |||dz                                  t          |                               t                      }|                                rt          |                                          D ]}|                                s|j        }t                              |          s9t          |          \  }}||z  }|                     t          ||dt          |          |||dz                                  t          |          |                                r|nd	  	                   | S )z4Return info for all profiles, including the default.rI   Tr   )rl   r   r   r   rO   r   r   r   FN)	rl   r   r   r   rO   r   r   r   r   )rk   rb   ru   r   appendr   r   r   r   rc   sortediterdirrl   rn   ro   )	r   r   default_homerO   r   profiles_rootentryrl   r   s	            r#   list_profilesr   ]  s   H"$$K ,--L ,\::x2<@@!F*2244%l33	
 	
 	
 		 		 		 '((M M113344 	 	E<<>> :D!''-- 077OE8$t+JOOK  6u = =!//11)%00)3):):)<)<F::$
 
 
 
 
 
 
 Or%   F
clone_from	clone_allclone_configno_aliasc                    t          |            | dk    rt          d          t          |           }|                                rt	          d|  d|           d}||s|r\|ddlm}  |            }nt          |           t          |          }|                                st          d|pd	 d
|           |rK|rIt          j
        ||t          |                     t          D ]}||z                      d           n|                    dd           t          D ]}	||	z                      dd           |t           D ]3}
||
z  }|                                rt          j        |||
z             4|dz  }|                                rt          j
        ||dz  d           t$          D ]Q}||z  }|                                r6||z  }|j                            dd           t          j        ||           R|dz  }|                                s/	 ddlm} |                    |d           n# t.          $ r Y nw xY w|S )a  Create a new profile directory.

    Parameters
    ----------
    name:
        Profile identifier (lowercase, alphanumeric, hyphens, underscores).
    clone_from:
        Source profile to clone from. If ``None`` and clone_config/clone_all
        is True, defaults to the currently active profile.
    clone_all:
        If True, do a full copytree of the source (all state).
    clone_config:
        If True, copy config files (config.yaml, .env, SOUL.md), installed
        skills, and selected profile identity files from the source profile.
    no_alias:
        If True, skip wrapper script creation.

    Returns
    -------
    Path
        The newly created profile directory.
    rI   uS   Cannot create a profile named 'default' — it is the built-in profile (~/.hermes).	Profile '' already exists at Nr   get_hermes_homezSource profile 'activez' does not exist at ignoreT
missing_okr   r   )dirs_exist_okr   )DEFAULT_SOUL_MDutf-8encoding)rq   r(   rs   r   FileExistsErrorrg   r   ru   r   shutilcopytreer,   _CLONE_ALL_STRIPr   r   _PROFILE_DIRS_CLONE_CONFIG_FILEScopy2_CLONE_SUBDIR_FILESparenthermes_cli.default_soulr   r   r   )rl   r   r   r   r   r   r   r   stalesubdirfilenamesrcsource_skillsrelpathdst	soul_pathr   s                    r#   create_profiler    s.   : $ya
 
 	
 "$''K SQ$QQKQQRRR Jl888888(**JJ!*---(44J  "" 	#[:#9[[z[[    %+Z %+-j99	
 	
 	
 	
 & 	: 	:E5 ((D(9999	: 	$666# 	F 	FF6!(((EEEE !/ > > 8+::<< >LkH&<=== '1M##%% [{X/EUYZZZZ / + + 7*::<< +%/CJ$$TD$AAALc*** i'I 	??????  7 CCCC 	 	 	D	 s   ,I
 

IIquietc                 
   t          t                    j        j                                        }	 t	          j        t          j        ddgi t          j	        dt          |           it          |          ddd          }|j        dk    rD|j                                        r+t          j        |j                                                  S |sat!          d|j                    |j                                        r1t!          d	|j                                        d
d                     d
S # t          j        $ r |st!          d           Y d
S t&          $ r}|st!          d|            Y d
}~d
S d
}~ww xY w)zSeed bundled skills into a profile via subprocess.

    Uses subprocess because sync_skills() caches HERMES_HOME at module level.
    Returns the sync result dict, or None on failure.
    z-cziimport json; from tools.skills_sync import sync_skills; r = sync_skills(quiet=True); print(json.dumps(r))HERMES_HOMET<   )envcwdr{   r|   r}   r   u%   ⚠ Skill seeding returned exit code z  N   u!   ⚠ Skill seeding timed out (60s)u   ⚠ Skill seeding failed: )r   __file__r   r&   r   r   sys
executabler   r   r+   r   r   r   jsonloadsr   stderrr   r   )r   r  project_rootr   r   s        r#   seed_profile_skillsr    s    >>(/7799L^TAB @2:?}c+.>.>??L!!dB
 
 
 !!fm&9&9&;&;!:fm1133444 	:M&:KMMNNN}""$$ :86=..00#688999t$    	75666tt    	42q22333ttttts%   BD7 A#D7 7 F	F#E==Fyesc                    t          |            | dk    rt          d          t          |           }|                                st	          d|  d          t          |          \  }}t          |          }t          |          }t          d|             t          d|            |rt          d| |rd| d	nd
z              |rt          d|            dg}t                      | z  }|
                                }	|	r|                    d| d	           t          d           |D ]}
t          d|
            |rt          d           |stt                       	 t          d|  d                                          }n(# t          t          f$ r t          d           |cY S w xY w|| k    rt          d           |S t!          | |           |rt#          |           |	r!t%          |           rt          d|            	 t'          j        |           t          d|            n,# t*          $ r}t          d| d|            Y d}~nd}~ww xY w	 t-                      }|| k    rt/          d           t          d           n# t*          $ r Y nw xY wt          d|  d           |S )zDelete a profile, its wrapper script, and its gateway service.

    Stops the gateway if running. Disables systemd/launchd service first
    to prevent auto-restart.

    Returns the path that was removed.
    rI   zZCannot delete the default profile (~/.hermes).
To remove everything, use: hermes uninstallr   ' does not exist.z

Profile: z	Path:    z	Model:   z (r   r   z	Skills:  z;All config, API keys, memories, sessions, skills, cron jobszCommand alias (z
This will permanently delete:u     • u0     ⚠ Gateway is running — it will be stopped.zType 'z' to confirm: z
Cancelled.z
Cancelled.u   ✓ Removed u   ⚠ Could not remove r   Nu#   ✓ Active profile reset to defaultz

Profile 'z
' deleted.)rq   r(   rs   ru   r   r   r   r   r   rk   r   r   inputr   KeyboardInterruptEOFError_cleanup_gateway_service_stop_gateway_processr   r   rmtreer   get_active_profileset_active_profile)rl   r  r   rO   r   
gw_runningr   itemsr   has_wrapperitemconfirmr   r   s                 r#   delete_profiler$    s    $y:
 
 	

 "$''K E CD C C CDDD )55OE8'44J,,K	


	
#k
#
#$$$ L!%!!%I%5(%5%5%5%5rJKKK )'+''((( 	FE
 $%%,L%%''K 86|666777	
,---  otoo CABBB  		9T999::@@BBGG!8, 	 	 	.!!!	 d??, T;///  +k***  1 && 	1///000:k"""*[**++++ : : :8k88Q8899999999:#%%T>>y)))7888    

(
(
(
()))s<   -%F "F87F8&H> >
I'I""I'+2J 
J+*J+c                 \   ddl }t          j                            d          }	 t	          |          t          j        d<   ddlm}m} |                                dk    r |            }t          j
                    dz  dz  dz  | d	z  }|                                r{t          j        d
dd|gddd           t          j        d
dd|gddd           |                    d           t          j        g dddd           t          d| d           n|                                dk    rk |            }|                                rMt          j        ddt	          |          gddd           |                    d           t          d           n)# t           $ r}	t          d|	            Y d}	~	nd}	~	ww xY w||t          j        d<   dS dt          j        v rt          j        d= dS dS # ||t          j        d<   ndt          j        v rt          j        d= w xY w)z9Disable and remove systemd/launchd service for a profile.r   Nr  )get_service_nameget_launchd_plist_pathLinuxz.configsystemduserz.service	systemctl--userdisableTF
   )r{   checkr}   stopr   )r+  r,  zdaemon-reloadu   ✓ Service z removedDarwin	launchctlunloadu   ✓ Launchd service removedu   ⚠ Service cleanup: )platformr   r   r   r+   hermes_cli.gatewayr&  r'  systemr   r   r   r   r   r   r   r   )
rl   r   	_platformold_homer&  r'  svc_namesvc_file
plist_pathr   s
             r#   r  r  s  s        z~~m,,H&*$'$4$4
=!OOOOOOOO((''))Hy{{Y.:VCF[F[F[[H   9 (Ix@#'ub     (FH=#'ub    4000<<<#'ub    7X7778888++//11J  "" 6 (C
OO<#'ub    !!T!2224555 + + +)a))********+ (0BJ}%%%bj((
=))) )( (0BJ}%%bj((
=)))))s0   E;F!  G< !
G+G=G< GG< </H+c                 F   ddl }ddl}| dz  }|                                sdS 	 |                                                                }|                    d          rt          j        |          ndt          |          i}t          |d                   }t          j
        ||j                   t          d          D ]R}|                    d           	 t          j
        |d           .# t          $ r t          d| d	           Y  dS w xY w	 t          j
        ||j                   n# t          $ r Y nw xY wt          d
| d	           dS # t          t"          f$ r t          d           Y dS t$          $ r}t          d|            Y d}~dS d}~ww xY w)z0Stop a running gateway process via its PID file.r   Nr   {pid   g      ?u   ✓ Gateway stopped (PID r   u   ✓ Gateway force-stopped (PID u   ✓ Gateway already stoppedu   ⚠ Could not stop gateway: )signaltimer   r   r   
startswithr  r  r   r   killSIGTERMrangesleepProcessLookupErrorr   SIGKILLPermissionErrorr   )	r   _signal_timepid_filerawdatar>  _r   s	            r#   r  r    s   ]*H?? 2  ""((**"%.."5"5Ltz#E3s88;L$u+
W_%%%r 	 	AKKQ%   8#888999	GC))))! 	 	 	D	6666777770 - - -+,,,,,, 2 2 20Q001111111112sf   B5E C10E 1DE DE D2 1E 2
D?<E >D??E  F :	F FF c                      t                      } 	 |                                                                 }|sdS |S # t          t          t
          f$ r Y dS w xY w)ztRead the sticky active profile name.

    Returns ``"default"`` if no active_profile file exists or it's empty.
    rI   )ri   r   r   r   UnicodeDecodeErrorr'   )r   rl   s     r#   r  r    sk    
 $%%D~~%%'' 	917;   yys   (< < AAc                    t          |            | dk    r$t          |           st          d|  d|            t                      }|j                            dd           | dk    r|                    d           d	S |                    d          }|                    | dz              |	                    |           d	S )
zlSet the sticky active profile.

    Writes to ``~/.hermes/active_profile``. Use ``"default"`` to clear.
    rI   r   8' does not exist. Create it with: hermes profile create Tr   r   .tmp
N)
rq   rv   r   ri   r   r   r   with_suffixr   replace)rl   r   rD   s      r#   r  r    s    
 $y!5!5< < <59< <
 
 	

 $%%DKdT222yt$$$$$ v&&td{###Dr%   c                     ddl m}   |             }|                                }t                                                      }||k    rdS t	                                                      }	 |                    |          }|j        }t          |          dk    r(t          	                    |d                   r|d         S n# t          $ r Y nw xY wdS )a%  Infer the current profile name from HERMES_HOME.

    Returns ``"default"`` if HERMES_HOME is not set or points to ``~/.hermes``.
    Returns the profile name if HERMES_HOME points into ``~/.hermes/profiles/<name>``.
    Returns ``"custom"`` if HERMES_HOME is set to an unrecognized path.
    r   r   rI   r   custom)rg   r   r&   rb   rc   relative_topartslenrn   ro   r(   )r   hermes_homeresolveddefault_resolvedr   relr[  s          r#   get_active_profile_namera    s     100000!/##K""$$H/1199;;###y&((0022M""=11	u::??~33E!H==?8O    8s   .AC 
CCroot_dirc                 >     dt           dt          dt          f fd}|S )zReturn an *ignore* callable for :func:`shutil.copytree`.

    At the root level it excludes everything in ``_DEFAULT_EXPORT_EXCLUDE_ROOT``.
    At all levels it excludes ``__pycache__``, sockets, and temp files.
    r   contentsr   c                 &   t                      }|D ]L}|dk    s|                    d          r|                    |           3|dv r|                    |           Mt          |           k    r|                    d |D                        |S )N__pycache__)z.sockrT  )zpackage.jsonzpackage-lock.jsonc              3   ,   K   | ]}|t           v |V  d S N)_DEFAULT_EXPORT_EXCLUDE_ROOT)r!   cs     r#   	<genexpr>z:_default_export_ignore.<locals>._ignore.<locals>.<genexpr>!  s-      TT!7S2S2S12S2S2S2STTr%   )setendswithaddr   rW   )r   rd  ignoredr   rb  s       r#   r*   z'_default_export_ignore.<locals>._ignore  s    uu 	# 	#E%%8I)J)J%E""""???E"""	??h&&NNTThTTTTTTr%   )r+   listrl  )rb  r*   s   ` r#   _default_export_ignorerq    s=    3 $ 3       Nr%   output_pathc                 B  	 ddl }t          |            t          |           }|                                st	          d|  d          t          |          }t          |                              d                              d          }| dk    r|                                5 }t          |          dz  }t          j
        ||t          |                     t          j        |d	|d          }t          |          cddd           S # 1 swxY w Y   |                                5 }t          |          | z  }d
dh	t          j
        ||	fd           t          j        |d	||           }t          |          cddd           S # 1 swxY w Y   dS )zMExport a profile to a tar.gz archive.

    Returns the output file path.
    r   Nr   r  z.tar.gzz.tgzrI   r   gztarrC   r   c                 (    t          |          z  S rh  )rl  )drd  _CREDENTIAL_FILESs     r#   <lambda>z export_profile.<locals>.<lambda>L  s    '83x=='H r%   )tempfilerq   rs   ru   r   r   r+   removesuffixTemporaryDirectoryr   r   rq  make_archive)
rl   rr  ry  r   outputbasetmpdirstagedr   rw  s
            @r#   export_profiler  '  s9   
 OOO$!$''K E CD C C CDDD+Fv;;##I..;;FCCDy ((** 	 f&\\I-FO-k::   
 (w	JJF<<	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  	  
	$	$	&	& 	&f$(&1HHHH	
 	
 	
 	

 $T7FDAAF||	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s&   )ADDD1AFFFmember_namec                 p   |                      dd          }t          |          }t          |           }|r/|                                s|                                s|j        rt          d|            d |j        D             }|rt          d |D                       rt          d|            |S )z4Return safe path parts for a profile archive member.\/zUnsafe archive member path: c                     g | ]}|d v|	S ))r   .r    r!   parts     r#   r$   z4_normalize_profile_archive_parts.<locals>.<listcomp>`  s"    HHHd$i2G2GT2G2G2Gr%   c              3   "   K   | ]
}|d k    V  dS )z..Nr    r  s     r#   rk  z3_normalize_profile_archive_parts.<locals>.<genexpr>a  s&      77777777r%   )rW  r   r   is_absolutedriver(   r[  any)r  normalized_name
posix_pathwindows_pathr[  s        r#    _normalize_profile_archive_partsr  R  s    !))$44O//J";//L G!!##G ##%%G 	G EEEFFFHHj.HHHE GC7777777 GEEEFFFLr%   archivedestinationc           	      *   ddl }|                    | d          5 }|                                D ]H}t          |j                  } |j        | }|                                r|                    dd           M|                                st          d|j                   |j
                            dd           |                    |          }|t          d|j                   |5  t          |d          5 }t          j        ||           ddd           n# 1 swxY w Y   ddd           n# 1 swxY w Y   	 t          j        ||j        d	z             9# t"          $ r Y Fw xY w	 ddd           dS # 1 swxY w Y   dS )
zAExtract a profile archive without allowing path escapes or links.r   Nr:gzTr   z!Unsupported archive member type: zCannot read archive member: wbi  )tarfiler   
getmembersr  rl   joinpathisdirr   isfiler(   r   extractfiler   copyfileobjr   r   moder'   )	r  r  r  tfmemberr[  target	extractedr  s	            r#   _safe_extract_profile_archiver  f  so   NNN	gv	&	& "mmoo 	 	F4V[AAE)[)51F||~~ TD999==??  EEE   Mt<<<v..I  !M!M!MNNN 3 3D.. 3#"9c2223 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3u!45555   /	                 s~   CF4D>D'	D>'D++D>.D+/D>2F>EFEF
E)'F)
E73F6E77FFFc                     ddl }|                    | d          5 }d |                                D             }|sd |                                D             }ddd           n# 1 swxY w Y   |S )a  Return the archive's top-level directory names.

    Profile imports expect exactly one root directory. Inspecting the archive
    before extraction lets us stage the import safely instead of mutating a
    live profile tree first and reconciling names later.
    r   Nr  c                     h | ]E}t          |j                  }t          |          d k    s|                                =|d         FS )r   r   )r  rl   r\  r  )r!   r  r[  s      r#   	<setcomp>z1_inspect_profile_archive_roots.<locals>.<setcomp>  sP     
 
 
:6;GG5zzA~~~ !H ~~r%   c                 j    h | ]0}|                                 t          |j                  d          1S )r   )r  r  rl   )r!   r  s     r#   r  z1_inspect_profile_archive_roots.<locals>.<setcomp>  sE       <<>>0==a@  r%   )r  r   r  )r  r  r  top_dirss       r#   _inspect_profile_archive_rootsr    s     NNN	gv	&	& "
 
--//
 
 
  	  mmoo  H               Os   ?A&&A*-A*archive_pathc                    ddl }t          |           }|                                st          d|           t	          |          }t          |          dk    r|                                nd}|p|}|st          d          |t          d          |dk    rt          d          t          |           t          |          }|                                rt          d	| d
|           t                      }|                    dd           |                    d          5 }	t          |	          }
t          ||
           |
|z  }|                                st          d|           |}||k    r|
|z  }|                    |           t#          j        t'          |          t'          |                     ddd           n# 1 swxY w Y   |S )zImport a profile from a tar.gz archive.

    If *name* is not given, infers it from the archive's top-level directory.
    Returns the imported profile directory.
    r   NzArchive not found: r   zpCannot determine profile name from archive. Specify it explicitly: hermes profile import <archive> --name <name>z=Profile archive must contain exactly one top-level directory.rI   u   Cannot import as 'default' — that is the built-in root profile (~/.hermes). Specify a different name: hermes profile import <archive> --name <name>r   r   Tr   hermes_profile_import_)prefixz,Profile archive root is missing or invalid: )ry  r   r   r   r  r\  popr(   rq   rs   r   rc   r   r{  r  ru   renamer   mover+   )r  rl   ry  r  r  archive_rootinferred_namer   r   r  staging_rootr  final_sources                r#   import_profiler    sm    OOO<  G>> A ?g ? ?@@@-g66H%(]]a%7%78<<>>>TL(LM 
S
 
 	
 K
 
 	
 	!!V
 
 	

 -(((!-00K \Z-ZZ[ZZ[[[&((Mt444		$	$,D	$	E	E 9F||%g|<<< </	!! 	M|MM   !=(('-7L\***C%%s;'7'78889 9 9 9 9 9 9 9 9 9 9 9 9 9 9" s   /BGGGold_namenew_namenew_dirc                    d|  }d| }|dz  t                      dz  t          j                    dz  dz  g}t                      }|D ]}	 |                                }n# t
          $ r |}Y nw xY w||v s|                                sD|                    |           	 t          j	        |
                    d                    }	n# t
          t          j        f$ r Y w xY w|	                    d          }
t          |
t                    r||
vr||
v rt          d| d	|            |
|         }t          |t                    r+d
|vr'd|v r|                    dd          d         n|}||d
<   |
                    |          |
|<   |                    |j        dz             }	 |                    t          j        |	dd          dz   d           |                    |           n9# t
          $ r, 	 |                    d           n# t
          $ r Y nw xY wY w xY wt          d| d|            dS )zGRename Honcho host blocks for a renamed profile without changing peers.zhermes.zhoncho.jsonz.honchozconfig.jsonr   r   hostsu$   ⚠ Honcho host block not migrated: z already exists in aiPeerr  r   rT     F)indentensure_asciirU  Tr   u   ✓ Honcho host updated:     → N)rb   r   r   rl  r&   r'   is_filern  r  r  r   JSONDecodeErrorr   r   r   r   r   r  rV  suffixr   dumpsrW  r   )r  r  r  old_hostnew_host
candidatesseenr   r^  rM  r  blockbarerD   s                 r#   _migrate_honcho_profile_hostr    s   ###H###H 	- ""]2	i-/J eeD &E &E	||~~HH 	 	 	HHH	t4<<>>	*T^^W^==>>CC-. 	 	 	H	   %&& 	(%*?*?u\\\VZ\\]]]heT"" 	#xu'<'<03x8>>#q))!,,XD"E(O))H--ht{V344	NN4:c!%HHH4OZaNbbbKK 	 	 	

d
++++   H	 	C(CCCCDDDDM&E &Es[   A""A10A1#(CC%$C%/AG44
H*?HH*
H# H*"H##H*)H*c                    t          |            t          |           | dk    rt          d          |dk    rt          d          t          |           }t          |          }|                                st	          d|  d          |                                rt          d| d          t          |          rt          | |           t          |           |
                    |           t          d|j         d|j                    t          | ||           t          |            t          |          }|s"t!          |           t          d	|            nt          d
| d|            	 t#                      | k    r!t%          |           t          d|            n# t&          $ r Y nw xY w|S )zrRename a profile: directory, wrapper script, service, active_profile.

    Returns the new profile directory.
    rI   z"Cannot rename the default profile.u.   Cannot rename to 'default' — it is reserved.r   r  z' already exists.u   ✓ Renamed r  u   ✓ Alias updated: u   ⚠ Cannot create alias 'u   ' — u   ✓ Active profile updated: )rq   r(   rs   ru   r   r   r   r   r  r  r  r   rl   r  r   r   r   r  r  r   )r  r  old_dirr  	collisions        r#   rename_profiler    s   
 (###(###9=>>>9IJJJh''Gh''G>> I GH G G GHHH~~ GE(EEEFFF g&& ' 7333g&&& NN7	
:
:
:GL
:
:;;; !8W=== (###%h//I Gh'''.H..////E(EE)EEFFF8++x(((;;;<<<    Ns    3F4 4
G Gc                      dS )z;Generate a bash completion script for hermes profile names.az  # Hermes Agent profile completion
# Add to ~/.bashrc: eval "$(hermes completion bash)"

_hermes_profiles() {
    local profiles_dir="$HOME/.hermes/profiles"
    local profiles="default"
    if [ -d "$profiles_dir" ]; then
        profiles="$profiles $(ls "$profiles_dir" 2>/dev/null)"
    fi
    echo "$profiles"
}

_hermes_completion() {
    local cur prev
    cur="${COMP_WORDS[COMP_CWORD]}"
    prev="${COMP_WORDS[COMP_CWORD-1]}"

    # Complete profile names after -p / --profile
    if [[ "$prev" == "-p" || "$prev" == "--profile" ]]; then
        COMPREPLY=($(compgen -W "$(_hermes_profiles)" -- "$cur"))
        return
    fi

    # Complete profile subcommands
    if [[ "${COMP_WORDS[1]}" == "profile" ]]; then
        case "$prev" in
            profile)
                COMPREPLY=($(compgen -W "list use create delete show alias rename export import" -- "$cur"))
                return
                ;;
            use|delete|show|alias|rename|export)
                COMPREPLY=($(compgen -W "$(_hermes_profiles)" -- "$cur"))
                return
                ;;
        esac
    fi

    # Top-level subcommands
    if [[ "$COMP_CWORD" == 1 ]]; then
        local commands="chat model gateway setup status cron doctor dump config skills tools mcp sessions profile update version"
        COMPREPLY=($(compgen -W "$commands" -- "$cur"))
    fi
}

complete -F _hermes_completion hermes
r    r    r%   r#   generate_bash_completionr  O  s    - -r%   c                      dS )z:Generate a zsh completion script for hermes profile names.a  #compdef hermes
# Hermes Agent profile completion
# Add to ~/.zshrc: eval "$(hermes completion zsh)"

_hermes() {
    local -a profiles
    profiles=(default)
    if [[ -d "$HOME/.hermes/profiles" ]]; then
        profiles+=("${(@f)$(ls $HOME/.hermes/profiles 2>/dev/null)}")
    fi

    _arguments \
        '-p[Profile name]:profile:($profiles)' \
        '--profile[Profile name]:profile:($profiles)' \
        '1:command:(chat model gateway setup status cron doctor dump config skills tools mcp sessions profile update version)' \
        '*::arg:->args'

    case $words[1] in
        profile)
            _arguments '1:action:(list use create delete show alias rename export import)' \
                        '2:profile:($profiles)'
            ;;
    esac
}

_hermes "$@"
r    r    r%   r#   generate_zsh_completionr    s     r%   profile_namec                     t          |            t          |           }| dk    r)|                                st          d|  d|            t	          |          S )zResolve a profile name to a HERMES_HOME path string.

    Called early in the CLI entry point, before any hermes modules
    are imported, to set the HERMES_HOME environment variable.
    rI   r   rS  )rq   rs   ru   r   r+   )r  r   s     r#   resolve_profile_envr    s     ,'''!,//Ky  ););)=)= D D D5AD D
 
 	

 {r%   )NFFF)Frh  )Er   r  r   rer   r   r   r  dataclassesr   pathlibr   r   r   typingr   r   compilern   r   r   r   r   r,   	frozensetri  r   r   rc   rb   ri   rk   r+   rq   rs   r   rv   r   r   r   r   r   tupler   r   r   r   r   r  r   r  r$  r  r  r  r  ra  rq  r  r  r  rl  r  r  r  r  r  r  r  r    r%   r#   <module>r     s   *  				 				       



 ! ! ! ! ! ! 8 8 8 8 8 8 8 8 8 8 ! ! ! ! ! ! ! !9::  "        4    4  )y * * *     2 )     
  i ! ! !   3D 3 3 3 3%$ % % % %9$ 9 9 9 9
*$ * * * *
 
 
 
 
 
'# '$ ' ' ' '* * * * * *     BG G G G G     ,     & 
& 
& 
& 
& 
& 
& 
& 
&D U    &     	t 	 	 	 	 	 +tK( + + + +` !%h h
hh h 	h
 h 
h h h hV T $ 8D>    BY Y Y4 YD Y Y Y Yx-*3 -*T -*d -* -* -* -*`2t 2 2 2 2 2LC    S T    0    >T    0( (3 (4 ( ( ( (V# $s)    (4 d t    @D SX    2; ; ;HSM ;T ; ; ; ;D2E3 2E# 2E 2EQU 2E 2E 2E 2Ej2S 2C 2D 2 2 2 2r/# / / / /d    Fc c      r%   