
    Pmj                    ~    d Z ddlmZ ddlZddlZddlmZ dZdZddZ	ddZ
ddZdddZddZd dZdeddd!dZdS )"a"  ``!<command>`` shell mode for the interactive CLI.

Typing ``!git status`` at the composer runs the command directly in the
session's working directory. The model is never invoked: no user message, no
assistant message, no tool result enters the conversation history, so a bang
command costs zero tokens and cannot perturb role alternation or the prompt
cache.

A user-typed command still goes through the SAME dangerous-pattern approval
gate the terminal tool uses (``tools.approval.check_all_command_guards``),
reached here through ``tools.terminal_tool._check_all_guards`` so the CLI
approval callback and Docker host-access handling behave identically.

CLI-only by design: gateway/API/cron sessions have their own shells and no
composer, so :func:`bang_shell_enabled` gates the feature off there.
    )annotationsN)OptionaluZ   Usage: !<command> — run a shell command without spending a model turn (e.g. !git status)x   textOptional[str]returnboolc                ~    t          | t                    sdS |                                                     d          S )a  Return True when *text* is a ``!`` shell-mode submission.

    Only a leading ``!`` (after surrounding whitespace) counts. A line that
    merely *contains* ``!`` mid-text (``fix the bug!``, ``echo hi!``) is an
    ordinary prompt and must reach the agent untouched.
    F!
isinstancestrstrip
startswith)r   s    ;/home/thesage/.hermes/hermes-agent/hermes_cli/bang_shell.pyis_bang_commandr       s7     dC   u::<<""3'''    r   c                    t          | t                    sdS |                                 }|                    d          sdS |dd                                         S )u  Return the shell command inside a bang submission (``""`` when bare).

    ``!ls`` → ``ls``; ``!  ls -la`` → ``ls -la``; ``!!`` → ``!`` (a literal
    second bang is part of the command, e.g. history expansion the user's
    shell will handle); ``!`` alone → ``""``.
     r      Nr   )r   strippeds     r   parse_bang_commandr   ,   s]     dC   rzz||Hs## rABB<r   c                     	 ddl m}  n# t          $ r d
d} Y nw xY w | d          rdS  | d          rdS t          j        d          pd                                rdS d	S )a  True only for interactive local CLI sessions.

    Gateway, API, and cron sessions never reach the composer and their users
    already have a shell; running arbitrary commands for them would be a
    remote-execution surface with no approving human at the keyboard.
    r   env_var_enabledr   c                    t          t          j        | |                                                                                    dv S )N>   1onyestrue)r   osgetenvr   lower)namedefaults     r   r   z+bang_shell_enabled.<locals>.env_var_enabledE   s:    ryw//006688>>@@D^^^r   HERMES_GATEWAY_SESSIONFHERMES_CRON_SESSIONHERMES_SESSION_PLATFORMT)r   )utilsr   	Exceptionr!   r"   r   r   s    r   bang_shell_enabledr+   ;   s    _))))))) _ _ _	_ 	_ 	_ 	_ 	_ 	__ /00 u,-- u
	+,,299;; u4s   	 session_keyc                    	 ddl m}m}  ||           }|r|S  |            pi                     d          }|r|S n# t          $ r Y nw xY wdS )a  Return the directory a bang command should run in.

    Mirrors the terminal tool's resolution order so ``!pwd`` matches where the
    agent's own commands land: the session's recorded ``cd`` state first
    (``terminal_tool.get_session_cwd``, updated after every agent command),
    then the configured ``TERMINAL_CWD``/backend default. ``None`` means "let
    the subprocess inherit the process cwd".
    r   )_get_env_configget_session_cwdcwdN)tools.terminal_toolr.   r/   getr*   )r,   r.   r/   recorded
configureds        r   resolve_bang_cwdr5   Q   s    
HHHHHHHH"?;// 	O%o''-222599
 		   4s   = "= 
A
	A
commanddictc                X    	 ddl m} n# t          $ r dddcY S w xY w || dd          S )	u  Run *command* through the terminal tool's approval gate.

    Reuses ``tools.terminal_tool._check_all_guards`` — the exact function
    ``terminal_tool()`` calls before executing anything — so the hardline
    blocklist, user deny rules, tirith findings, and the interactive
    dangerous-command prompt all apply to user-typed bang commands too. A
    command the agent would need approval for still needs approval when the
    user types it; ``!`` is a latency/cost shortcut, not a security bypass.

    Returns the gate's decision dict (``{"approved": bool, "message": ...}``).
    Falls back to *approved* only when the gate itself cannot be imported,
    which would mean a broken install rather than a policy decision.
    r   )_check_all_guardsTN)approvedmessagelocalF)has_host_access)r1   r9   r*   )r6   r9   s     r   check_bang_approvalr>   h   se    39999999 3 3 3 T222223
 WguEEEEs   	 c                     	 ddl m}   | t          j                                                  S # t
          $ r! t          j                                        cY S w xY w)a  Environment for a bang command, with Hermes-managed secrets filtered.

    The CLI process holds every configured provider API key in ``os.environ``.
    A bang command is user-typed, but it can still be a third-party script, so
    reuse the same sanitizer ``quick_commands`` and the local terminal backend
    use rather than handing the whole keyring to an arbitrary subprocess.
    r   _sanitize_subprocess_env)tools.environments.localrA   r!   environcopyr*   r@   s    r   	_bang_envrE      si    !EEEEEE''
(9(9::: ! ! !z     !s   ,/ (AA)r0   timeoutwriterr0   rF   intc                  |pd }|r>t           j                            t           j                            |                    r|nd}|rt           j                            |          }	 ddlm}  |            }n# t          $ r d}Y nw xY w	 t          j        | dt          j	        t          j
        ddd|t                      |
  
        }n&# t          $ r}	 |d	|	            Y d}	~	d
S d}	~	ww xY w	 |j        (|j        D ] }
 ||
                    d                     !|                    |           n# t          j        $ r[ |                                  |d| d           Y 	 |j        |j                                         dS dS # t          $ r Y dS w xY wt$          $ rW |                                  |d           Y 	 |j        |j                                         dS dS # t          $ r Y dS w xY ww xY w	 	 |j        |j                                         nG# t          $ r Y n;w xY w# 	 |j        |j                                         w w # t          $ r Y w w xY wxY wt'          |j        pd          S )u|  Execute *command* and stream its output, returning the exit code.

    stdout and stderr are merged and written through *writer* (defaults to
    ``print``) as they arrive, so long-running commands show progress instead
    of buffering to the end. Nothing is returned to a caller for insertion
    into conversation history — the output exists only on the user's terminal.
    c                R    t          | |                     d          rdnd          S )N
r   )end)printendswith)lines    r   <lambda>z"run_bang_command.<locals>.<lambda>   s)    5t9L9L3V22RV#W#W#W r   Nr   )windows_hide_flagsTzutf-8replace)	shellstdoutstderrr   encodingerrorsr0   envcreationflagsz!: failed to run command:    rK   )rF   z!: command timed out after s|   z!: interrupted   )r!   pathisdir
expanduserhermes_cli._subprocess_compatrQ   r*   
subprocessPopenPIPESTDOUTrE   rT   rstripwaitTimeoutExpiredkillcloseKeyboardInterruptrH   
returncode)r6   r0   rF   rG   emitrun_cwdrQ   rY   procexcrO   s              r   run_bang_commandrq      sY    XWWDObgmmBG,>,>s,C,CDDOcc4G .'$$W--DDDDDD**,,    ?$'
 
 
    /#//000sssss;" ( (T[[&&''''		'	""""$   		57555666	{&!!##### '& 	 	 	DD	    			{&!!##### '& 	 	 	DD	 	#	{&!!### 	 	 	D		{&!!#### ' 	 	 	D	 t#!$$$s   *A; ;B
	B
>C 
C0C++C04AD: 9H9 :2H,H9 . F 
F F #(HH9  G1 1
G?>G?HH9  H) )
H65H69I-; II-
I*'I-)I**I-)r   r   r   r	   )r   r   r   r   )r   r	   )N)r,   r   r   r   )r6   r   r   r7   )r   r7   )r6   r   r0   r   rF   rH   r   rH   )__doc__
__future__r   r!   rb   typingr   
USAGE_HINTDEFAULT_TIMEOUTr   r   r+   r5   r>   rE   rq    r   r   <module>rx      s   " # " " " " " 				          i

 	( 	( 	( 	(          ,    .F F F F0! ! ! !& "C% C% C% C% C% C% C% C%r   