
    i                        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 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.	 	 	 	 dDded$ee         d%e"d&e"d'e"defd(Z/dEded)e"dee0         fd*Z1dEded+e"defd,Z2dededdfd-Z3deddfd.Z4defd/Z5deddfd0Z6defd1Z7d2efd3Z8ded4edefd5Z9d6edee         fd7Z:d8ed9eddfd:Z;dFd;edee         defd<Z<d=ed>edefd?Z=defd@Z>defdAZ?dBedefdCZ@dS )GuW  
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
    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>   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   profiles	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	uninstallreturnc                  $    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_home     ;/home/agentuser/.hermes/hermes-agent/hermes_cli/profiles.py_get_profiles_rootrS   x   s     $%%
22rQ   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_constantsrV   rU   s    rR   rO   rO      s%     988888""$$$rQ   c                  $    t                      dz  S )z2Return the path to the sticky active_profile file.r,   rN   rP   rQ   rR   _get_active_profile_pathrY      s    #%%(888rQ   c                  4    t          j                    dz  dz  S )z)Return the directory for wrapper scripts.z.localr%   )r   r   rP   rQ   rR   _get_wrapper_dirr[      s    9;;!E))rQ   namec                 p    | dk    rdS t                               |           st          d| d          dS )zARaise ``ValueError`` if *name* is not a valid profile identifier.r5   NzInvalid profile name z%. Must match [a-z0-9][a-z0-9_-]{0,63})_PROFILE_ID_REmatch
ValueErrorr\   s    rR   validate_profile_namerb      sZ    y%% 
*D * * *
 
 	

 
rQ   c                 L    | dk    rt                      S t                      | z  S )z4Resolve a profile name to its HERMES_HOME directory.r5   )rO   rS   ra   s    rR   get_profile_dirrd      s*    y')))$&&rQ   c                 T    | dk    rdS t          |                                           S )z)Check whether a profile directory exists.r5   T)rd   is_dirra   s    rR   profile_existsrg      s,    yt4  '')))rQ   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_SUBCOMMANDSr[   
subprocessrun
returncodestdoutstripstr	read_text	ExceptionFileNotFoundErrorTimeoutExpired)r\   wrapper_dirresultexisting_pathcontents        rR   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 )rx   r[   osenvirongetsplitpathsep)r}   s    rR   _is_wrapper_dir_in_pathr      sA    &(())K"*..44::2:FFFFrQ   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 )r[   mkdirOSErrorprint
write_textchmodstatst_modeS_IEXECS_IXGRPS_IXOTH)r\   r}   ewrapper_paths       rR   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.ro   TF)r[   existsry   unlinkrz   )r\   r   r   s      rR   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.r\   path
is_defaultgateway_runningNr;   providerFhas_envr   skill_count
alias_path)__name__
__module____qualname____doc__rx   __annotations__r   boolr;   r   r   r   r   intr   rP   rQ   rR   r   r     s         ..
III
JJJE8C="Hhsm"""GTK!%J%%%%%rQ   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rr;   r5   r   )	r   yamlopen	safe_loadr   
isinstancerx   dictrz   )r   config_pathr   fcfg	model_cfgs         rR   _read_config_modelr     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           	         | dz  }|                                 sdS 	 |                                                                }|sdS |                    d          rt	          j        |          ndt          |          i}t          |d                   }t          j        |d           dS # t          j	        t          t          t          t          t          t          f$ r Y dS w xY w)z<Check if a gateway is running for a given profile directory.r   F{pidr   T)r   ry   rw   
startswithjsonloadsr   r   killJSONDecodeErrorKeyErrorr`   	TypeErrorProcessLookupErrorPermissionErrorr   )r   pid_filerawdatar   s        rR   _check_gateway_runningr   -  s    ]*H?? u
  ""((** 	5"%.."5"5Ltz#E3s88;L$u+
Qt (J	:   uus   (B- A$B- -4C%$C%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/   )rf   rglobrx   )r   
skills_dircountmds       rR   _count_skillsr   ?  sw    x'J qEz**  3r77""xs2ww'>'>QJELrQ   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.r5   Tr   )r\   r   r   r   r;   r   r   r   FN)	r\   r   r   r   r;   r   r   r   r   )r[   rO   rf   r   appendr   r   r   r   rS   sortediterdirr\   r^   r_   )	r&   r}   default_homer;   r   profiles_rootentryr\   r   s	            rR   list_profilesr   O  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::$
 
 
 
 
 
 
 OrQ   F
clone_from	clone_allclone_configno_aliasc                 2   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
|           |r;|r9t          j
        ||           t          D ]}||z                      d           n|                    dd           t          D ]}	||	z                      dd           |t          D ]3}
||
z  }|                                rt          j        |||
z             4t"          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 )ac  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 only config files (config.yaml, .env, SOUL.md).
    no_alias:
        If True, skip wrapper script creation.

    Returns
    -------
    Path
        The newly created profile directory.
    r5   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 T
missing_okr   r   )DEFAULT_SOUL_MDzutf-8)encoding)rb   r`   rd   r   FileExistsErrorrW   r   rf   r{   shutilcopytree_CLONE_ALL_STRIPr   r   _PROFILE_DIRS_CLONE_CONFIG_FILEScopy2_CLONE_SUBDIR_FILESparenthermes_cli.default_soulr   r   rz   )r\   r   r   r   r   r   
source_dirr   stalesubdirfilenamesrcrelpathdst	soul_pathr   s                   rR   create_profiler   }  s   8 $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 +
K000% 	: 	:E5 ((D(9999	: 	$666# 	F 	FF6!(((EEEE !/ > > 8+::<< >LkH&<=== / + + 7*::<< +%/CJ$$TD$AAALc*** i'I 	??????  7 CCCC 	 	 	D	 s   )H 
HH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cwdrl   rm   rn   r   u%   ⚠ Skill seeding returned exit code z  N   u!   ⚠ Skill seeding timed out (60s)u   ⚠ Skill seeding failed: )r   __file__r   resolvers   rt   sys
executabler   r   rx   ru   rv   rw   r   r   r   stderrr|   rz   )r   r   project_rootr~   r   s        rR   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.
    r5   zZCannot delete the default profile (~/.hermes).
To remove everything, use: hermes uninstallr   ' does not exist.z

Profile: z	Path:    z	Model:   z (rp   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.)rb   r`   rd   rf   r{   r   r   r   r   r[   r   r   inputrw   KeyboardInterruptEOFError_cleanup_gateway_service_stop_gateway_processr   r   rmtreerz   get_active_profileset_active_profile)r\   r  r   r;   r   
gw_runningr   itemsr   has_wrapperitemconfirmr   r   s                 rR   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
   )rl   checkrn   stopr   )r#  r$  zdaemon-reloadu   ✓ Service z removedDarwin	launchctlunloadu   ✓ Launchd service removedu   ⚠ Service cleanup: )platformr   r   r   rx   hermes_cli.gatewayr  r  systemr   r   r   rs   rt   r   r   rz   )
r\   r   	_platformold_homer  r  svc_namesvc_file
plist_pathr   s
             rR   r  r  X  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   r   r      g      ?u   ✓ Gateway stopped (PID rp   u   ✓ Gateway force-stopped (PID u   ✓ Gateway already stoppedu   ⚠ Could not stop gateway: )signaltimer   ry   rw   r   r   r   r   r   r   SIGTERMrangesleepr   r   SIGKILLr   rz   )	r   _signal_timer   r   r   r   _r   s	            rR   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.
    r5   )rY   ry   rw   r{   UnicodeDecodeErrorr   )r   r\   s     rR   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.
    r5   r   8' does not exist. Create it with: hermes profile create Tr   r   .tmp
N)
rb   rg   r{   rY   r   r   r   with_suffixr   replace)r\   r   r0   s      rR   r  r    s    
 $y!5!5< < <59< <
 
 	

 $%%DKdT222yt$$$$$ v&&td{###DrQ   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   r5   r   custom)rW   r   r  rO   rS   relative_topartslenr^   r_   r`   )r   hermes_homeresolveddefault_resolvedr   relrJ  s          rR   get_active_profile_namerP    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.
    	directorycontentsrL   c                 &   t                      }|D ]L}|dk    s|                    d          r|                    |           3|dv r|                    |           Mt          |           k    r|                    d |D                        |S )N__pycache__)z.sockrC  )zpackage.jsonzpackage-lock.jsonc              3   ,   K   | ]}|t           v |V  d S N)_DEFAULT_EXPORT_EXCLUDE_ROOT).0cs     rR   	<genexpr>z:_default_export_ignore.<locals>._ignore.<locals>.<genexpr>  s-      TT!7S2S2S12S2S2S2STTrQ   )setendswithaddr   rC   )rS  rT  ignoredr   rQ  s       rR   _ignorez'_default_export_ignore.<locals>._ignore  s    uu 	# 	#E%%8I)J)J%E""""???E"""	??h&&NNTThTTTTTTrQ   )rx   listr]  )rQ  ra  s   ` rR   _default_export_ignorerc    s=    3 $ 3       NrQ   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.tgzr5   )ignoregztarr/   r   c                 (    t          |          z  S rX  )r]  )drT  _CREDENTIAL_FILESs     rR   <lambda>z export_profile.<locals>.<lambda>1  s    '83x=='H rQ   )tempfilerb   rd   rf   r{   r   rx   removesuffixTemporaryDirectoryr   r   rc  make_archive)
r\   rd  rl  r   outputbasetmpdirstagedr~   rj  s
            @rR   export_profilert    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   .rP   rZ  parts     rR   
<listcomp>z4_normalize_profile_archive_parts.<locals>.<listcomp>E  s"    HHHd$i2G2GT2G2G2GrQ   c              3   "   K   | ]
}|d k    V  dS )z..NrP   r{  s     rR   r\  z3_normalize_profile_archive_parts.<locals>.<genexpr>F  s&      77777777rQ   )rF  r   r   is_absolutedriver`   rJ  any)ru  normalized_name
posix_pathwindows_pathrJ  s        rR    _normalize_profile_archive_partsr  7  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LrQ   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  r\   joinpathisdirr   isfiler`   r   extractfiler   copyfileobjr   r   moder   )	r  r  r  tfmemberrJ  target	extractedr   s	            rR   _safe_extract_profile_archiver  K  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archive_pathc                 ~   ddl }t          |           }|                                st          d|           |                    |d          5 }d |                                D             }|sd |                                D             }ddd           n# 1 swxY w Y   |p(t          |          dk    r|                                nd}|st          d          |d	k    rt          d
          t          |           t          |          }|                                rt          d| d|           t                      }|                    dd           t          ||           ||r|                                n|z  }	|	|k    r)|	                                r|	                    |           |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  c                     h | ]E}t          |j                  }t          |          d k    s|                                =|d         FS )r   r   )r  r\   rK  r  )rZ  r  rJ  s      rR   	<setcomp>z!import_profile.<locals>.<setcomp>y  sP     
 
 
:6;GG5zzA~~~ !H ~~rQ   c                 j    h | ]0}|                                 t          |j                  d          1S )r   )r  r  r\   )rZ  r  s     rR   r  z!import_profile.<locals>.<setcomp>  sE       <<>>0==a@  rQ   r   zpCannot determine profile name from archive. Specify it explicitly: hermes profile import <archive> --name <name>r5   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   )r  r   r   r{   r   r  rK  popr`   rb   rd   r   rS   r   r  rename)
r  r\   r  r  r  top_dirsinferred_namer   r   r  s
             rR   import_profiler  k  sG    NNN<  G>> A ?g ? ?@@@ 
gv	&	& "
 
--//
 
 
  	  mmoo  H               Ls8}}/A/AX\\^^^tM 
S
 
 	
 	!!V
 
 	

 -(((!-00K \Z-ZZ[ZZ[[[&((Mt444!'=999 8!NOIKI$4$4$6$6%%%s   ?BB"Bold_namenew_namec                 j   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          |          }|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.
    r5   z"Cannot rename the default profile.u.   Cannot rename to 'default' — it is reserved.r   r  z' already exists.u   ✓ Renamed u    → u   ✓ Alias updated: u   ⚠ Cannot create alias 'u   ' — u   ✓ Active profile updated: )rb   r`   rd   rf   r{   r   r   r   r  r  r  r   r\   r   r   r   r  r  rz   )r  r  old_dirnew_dir	collisions        rR   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
:
:;;; (###%h//I Gh'''.H..////E(EE)EEFFF8++x(((;;;<<<    Ns   /3F# #
F0/F0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
rP   rP   rQ   rR   generate_bash_completionr    s    - -rQ   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 "$@"
rP   rP   rQ   rR   generate_zsh_completionr    s     rQ   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.
    r5   r   rB  )rb   rd   rf   r{   rx   )r  r   s     rR   resolve_profile_envr  7  s     ,'''!,//Ky  ););)=)= D D D5AD D
 
 	

 {rQ   )NFFF)FrX  )Ar   r   r   rer   r   rs   r  dataclassesr   pathlibr   r   r   typingr   r   compiler^   r   r   r   r   	frozensetrY  rq   rr   rS   rO   rY   r[   rx   rb   rd   r   rg   r   r   r   r   r   tupler   r   r   r   r   r   r   r  r  r  r  r  r  rP  rc  rt  r  r  r  r  r  r  r  rP   rQ   rR   <module>r     sJ   *  				 				       



 ! ! ! ! ! ! 8 8 8 8 8 8 8 8 8 8 ! ! ! ! ! ! ! !9::  "          )y * * *     2 )     
  i ! ! !   3D 3 3 3 3%$ % % % %9$ 9 9 9 9
*$ * * * *
 
 
 
 
 
'# '$ ' ' ' '* * * * * *     BG G G G G     ,     & 
& 
& 
& 
& 
& 
& 
& 
&D U    &     $	t 	 	 	 	 	 +tK( + + + +` !%[ [
[[ [ 	[
 [ 
[ [ [ [| 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    @: : :HSM :T : : : :B/S /C /D / / / /l/# / / / /d    Fc c      rQ   