
    Pi;|                       U d Z ddlmZ ddlZddlZddlZddlmZmZm	Z	 ddl
mZ  ej                    Zedz  dz  ZdZdZ G d	 d
e          ZdZdZdZe G d d                      Ze G d d                      Ze G d d                      Ze G d d                      ZdkdZdldZdmd!Zdnd#Zdod$Zdpdqd'Zdrd*Z dsd,Z!dtd.Z"dud3Z#d4Z$d5Z%d6Z&d7Zd8Z'd9Z(d:Z)dvd<Z*d=d>dwdAZ+dvdBZ,dxdCZ-dydDZ.dzdGZ/d{dHZ0d|dIZ1d}dKZ2d~dLZ3ddMZ4ddOZ5d=dPddSZ6d{dTZ7ddVZ8d=dPddXZ9ddYZ:ddZZ;dd\Z<dd]Z= ed^_           G d` da                      Z>da?dbe@dc<   d=aAd@e@dd<   ddeZB	 	 	 dddjZCdS )zDippy configuration system v1.    )annotationsN)	dataclassfieldreplace)Pathz.dippyconfigDIPPY_CONFIGc                      e Zd ZdZdS )ConfigErrorz<Raised when config loading fails due to I/O or parse errors.N)__name__
__module____qualname____doc__     2/root/projects/gits/Dippy/src/dippy/core/config.pyr   r      s        FFDr   r   userprojectenvc                  `    e Zd ZU dZded<   ded<   dZded<   dZded<   dZded	<   d
Zded<   dS )Rulez*A single config rule with origin tracking.strdecisionpatternN
str | NonemessagesourcescopeFboolexact)	r   r   r   r   __annotations__r   r   r   r    r   r   r   r   r      sn         44MMMLLLGFEEr   r   c                     e Zd ZU dZ ee          Zded<   	  ee          Zded<   	  ee          Z	ded<   	  ee          Z
ded<   	  ee          Zded<   	  ee          Zd	ed
<   	 dZded<   dZded<   dZded<   dS )ConfigzParsed configuration.default_factoryz
list[Rule]rulesredirect_rulesafter_rules	mcp_rulesafter_mcp_ruleszdict[str, str]aliasesaskr   defaultNPath | NonelogFr   log_full)r   r   r   r   r   listr&   r!   r'   r(   r)   r*   dictr+   r-   r/   r0   r   r   r   r#   r#   +   s        d333E3333&!&t!<!<!<N<<<<'#eD999K9999/!E$777I7777'"'%"="="=O====@#eD999G9999JGCHr   r#   c                  R    e Zd ZU dZded<   ded<   dZded<   dZded<   dZded	<   dS )
Matchz(Result of matching against config rules.r   r   r   Nr   r   r   r   )r   r   r   r   r!   r   r   r   r   r   r   r4   r4   F   s]         22MMMLLLGFEr   r4   c                  B    e Zd ZU dZded<   	  ee          Zded<   dS )SimpleCommandzA simple command extracted from parsed bash.

    This is the intermediate representation passed to the rule engine.
    Dippy parses raw bash with Parable, walks the AST, and constructs
    SimpleCommand instances for each command node.
    	list[str]wordsr$   	redirectsN)r   r   r   r   r!   r   r1   r9   r   r   r   r6   r6   Q   sK           2 5666I6666GGr   r6   cwdr   returnr.   c                    |                                  }	 |t          z  }|                                r|S |j        }||k    rdS |}2)z%Walk up from cwd to find .dippy file.TN)resolvePROJECT_CONFIG_NAMEis_fileparent)r:   current	candidater@   s       r   _find_project_configrC   d   sZ    kkmmG11	 	W4r   baseoverlayc                L   t          | | j        |j        z   | j        |j        z   | j        |j        z   | j        |j        z   | j        |j        z   i | j        |j        |j        dk    r|j        n| j        |j        |j        n| j        |j	        r|j	        n| j	        
  
        S )zMMerge overlay config into base. Rules accumulate in order, settings override.r,   N	r&   r'   r(   r)   r*   r+   r-   r/   r0   )
r   r&   r'   r(   r)   r*   r+   r-   r/   r0   )rD   rE   s     r   _merge_configsrH   q   s    j7=(*W-CC$w'::.7#44,w/FF34<37?3#*?e#;#;";2GKK%,%5H!!4=   r   r   r   r   c           	         t          | fd| j        D             fd| j        D             fd| j        D             fd| j        D             fd| j        D                       S )z3Tag all rules in config with source file and scope.c                4    g | ]}t          |           S )r   r   r   .0rr   r   s     r   
<listcomp>z_tag_rules.<locals>.<listcomp>   s(    LLL!wqu555LLLr   c                4    g | ]}t          |           S rK   rL   rM   s     r   rP   z_tag_rules.<locals>.<listcomp>   s5     
 
 
78GAfE222
 
 
r   c                4    g | ]}t          |           S rK   rL   rM   s     r   rP   z_tag_rules.<locals>.<listcomp>   s5     
 
 
78GAfE222
 
 
r   c                4    g | ]}t          |           S rK   rL   rM   s     r   rP   z_tag_rules.<locals>.<listcomp>   s(    TTTa71V5999TTTr   c                4    g | ]}t          |           S rK   rL   rM   s     r   rP   z_tag_rules.<locals>.<listcomp>   s5     
 
 
78GAfE222
 
 
r   )r&   r'   r(   r)   r*   )r   r&   r'   r(   r)   r*   )r   r   r   s    ``r   
_tag_rulesrU      s    LLLLLv|LLL
 
 
 
 
<B<Q
 
 

 
 
 
 
<B<N
 
 
 UTTTT6CSTTT
 
 
 
 
<B<R
 
 
   r   pathc                    	 |                                  }nD# t          $ r t          d|            dt          $ r}t          d|  d|           dd}~ww xY wt	          |t          |                     S )z@Read and parse a config file. Raises ConfigError on I/O failure.z"permission denied reading config: Nzcannot read config : )r   )	read_textPermissionErrorr   OSErrorparse_configr   )rV   textes      r   _load_config_filer_      s    G~~ Q Q QEtEEFFDP G G G;;;;;<<$FGSYY////s    &AAAc                b   t                      }	 t                                          rLt          t                    }t	          |t          t                    t                    }t          ||          }n&# t          $ r t          dt                     dw xY wt          |           }|Bt          |          }t	          |t          |          t                    }t          ||          }t          j                            t                    }|rt!          |                                          }	 |                                rBt          |          }t	          |t          |          t$                    }t          ||          }n!# t          $ r t          d|           dw xY w|S )zLoad config from ~/.dippy/config, .dippy, and $DIPPY_CONFIG.

    Raises ConfigError if any config file exists but cannot be read or parsed.
    Missing files are silently skipped.
    zpermission denied accessing N)r#   USER_CONFIGr?   r_   rU   r   
SCOPE_USERrH   rZ   r   rC   SCOPE_PROJECTosenvironget
ENV_CONFIGr   
expanduser	SCOPE_ENV)r:   r   user_configproject_pathproject_configenv_pathenv_config_path
env_configs           r   load_configrp      s    XXFR   	9+K88K$[#k2B2BJOOK#FK88F R R RFFFGGTQR (,,L*<88#NC4E4E}UU77 z~~j))H 
x..3355	&&(( <.??
'
C4H4H)TT
'
;; 	 	 	@@@ 	
 Ms   A%A6 6#B7AF F,r]   r   c                n   ddl }g }g }g }g }g }i }i }	|r| dnd}
t          |                                 d          D ]$\  }}|                                }|r|                    d          r2|                    dd          }|d                                         }t          |          dk    r|d                                         nd}	 |dk    rW|st          d          t          |          \  }}|
                    t          dt          |          |	                     n|d
k    rj|st          d          t          |          \  }}t          |          \  }}|
                    t          d
t          |          ||                     n|dk    rj|st          d          t          |          \  }}t          |          \  }}|
                    t          dt          |          ||                     n|dk    rC|st          d          |
                    t          dt          |                               n|dk    rW|st          d          t          |          \  }}|
                    t          d
t          |          |                     ny|dk    rW|st          d          t          |          \  }}|
                    t          dt          |          |                     n|dk    rJ|st          d          t          |          \  }}|
                    t          d||                     n|dk    r6|st          d          |
                    t          d|                     n|dk    rJ|st          d          t          |          \  }}|
                    t          d
||                     n@|dk    rI|st          d          t          |          \  }}|
                    t          d||                     n|dk    rI|st          d          t          |          \  }}|
                    t          d||                     n|dk    rr|                                }t          |          dk    rt          d          |\  }}t          |          }||v r|                    |
 d| d| d           |||<   n*|dk    rt          |	|           nt          d| d          # t          $ r)}|                    |
 d| d| d           Y d}~d}~ww xY wt!          |||||||	                    d d
          |	                    d!          |	                    d"d#          $	  	        S )%zCParse config text into Config object. Logs and skips invalid lines.r   NrX       #allowzrequires a pattern)r    r,   )r   r    denyzallow-redirectzask-redirect)r   zdeny-redirectafterz	allow-mcpzask-mcpzdeny-mcpz	after-mcpalias   z-requires exactly two arguments: source targetzline z	: alias 'z' redefined, overwritingsetzunknown directive ''z
 (skipped)r-   r/   r0   FrG   )logging	enumerate
splitlinesstrip
startswithsplitlowerlen
ValueError_strip_exact_anchorappendr   _expand_pattern_tildes_extract_messagewarning_apply_settingr#   rf   )r]   r   r|   r&   r'   r(   r)   r*   r+   settingsprefixlinenoraw_linelineparts	directiverestr   is_exactr   alias_sourcealias_targetexpanded_sourcer^   s                           r   r\   r\      s   NNNE!#N KI"$O G35H$,]]]]"F%doo&7&7;; tE tE~~ 	ts++ 	

4##!HNN$$	#&u::>>uQx~~rk	EG## ;$%9:::$7$=$=!"8"A"ARRR    e## ;$%9:::#3D#9#9 $7$@$@!.w77 '&	      f$$ ;$%9:::#3D#9#9 $7$@$@!.w77 '&	      ... ;$%9:::%%d74J44P4P&Q&QRRRRn,, ;$%9:::#3D#9#9 %% 6w ? ?QQQ    o-- ;$%9:::#3D#9#9 %%!7!@!@'RRR    g%% ;$%9:::#3D#9#9 ""4'#J#J#JKKKKk)) ;$%9:::  gt!4!45555i'' ;$%9:::#3D#9#9   eWg!F!F!FGGGGj(( ;$%9:::#3D#9#9   fgw!G!G!GHHHHk)) ;$%9:::#3D#9#9 &&tGWg'N'N'NOOOOg%%

u::??$%TUUU-2*l"8"F"F"g--OO! & & & & & & &   ,8((e##x.... !!Cy!C!C!CDDD 	E 	E 	EOOvCCFCCaCCCDDDDDDDD	E %'Y..LLj%00
 
 
 
s   QT..
U!8UU!r   tuple[str, bool]c                p    |                      d          r| dd                                         dfS | dfS )z8Strip | anchor from pattern, return (pattern, is_exact).|NTF)endswithrstripr   s    r   r   r   Z  sA     +ss|""$$d**E>r   sc                X   g }d}|t          |           k     r| |         dk    r@|dz   t          |           k     r*| |dz            }|dv r|                    |           |dz  }_|                    | |                    |dz  }|t          |           k     d                    |          S )z1Unescape backslash sequences in a message string.r   \rs   )"r   ry   rr   )r   r   join)r   resulti	next_chars       r   	_unescaper   a  s    F	A
c!ff**Q44<<AECFFNN!a%IK''i(((Qad	Q c!ff** 776??r   tuple[str, str | None]c                <   |                                  } |                     d          s| dfS t          |           dz
  }d}|dk    r(| |         dk    r|dz  }|dz  }|dk    r| |         dk    |dz  dk    r| dfS t          |           dz
  }|dk    r| |         dk    rn|dk    s| |dz
                                           rKt	          | |dz   d                   }| d|                                          }|st          d          ||fS |dz  }|dk    | dfS )	zExtract pattern and optional quoted message from string.

    Message is extracted only if:
    - String ends with unescaped "
    - There's an opening " preceded by whitespace

    Returns (pattern, message) where message may be None.
    r   Nry   r   r   rs   r   zpattern required before message)r   r   r   isspacer   r   )r   jnum_bsr   r   r   s         r   r   r   q  sO    	


A::c?? $w 	A
AF
q&&QqTT\\!	Q q&&QqTT\\ zQ$w 	A
A
q&&Q43;;AFFaAh&6&6&8&8F!a%"*..GellnnG D !BCCCG##	Q q&& d7Nr   r   "dict[str, bool | int | str | Path]r   Nonec                   |st          d          |                    dd          }|d                                         }t          |          dk    r|d         nd}|                    dd          }|dv r|t          d| d	          d
| |<   dS |dk    r|dvrt          d| d          || |<   dS |dk    r7|t          d          t          |                                          | |<   dS t          d| d          )zHParse and apply a 'set' directive. Raises ValueError on invalid setting.z'set' requires a setting nameNrs   r   -_)r0   r{   z' takes no valueTr-   )ru   r,   z)'default' must be 'allow' or 'ask', got 'r/   z'log' requires a pathzunknown setting ')r   r   r   r   r   r   rh   )r   r   r   keyvaluekey_normalizeds         r   r   r     sL    :8999JJtQE
(..

CE

QE!HHDE[[c**N &&6666777#'    
9	$	$(((QQQQRRR#(    
5	 	 =4555#';;#9#9#;#;    3S333444r   urlvariableabsolutehome	user_homerelativebaretokenc                   d| v rt           S |                     d          rt          S |                     d          rt          S | dk    s|                     d          rt          S |                     d          rt
          S | dv s.|                     d          s|                     d          sd| v rt          S t          S )	zClassify a token into one of the path kinds.

    Classification is pure - no side effects, no cwd needed.
    Order matters: earlier checks take precedence.
    z://$/~z~/).z..z./z../)_URLr   	_VARIABLE	_ABSOLUTE_HOME
_USER_HOME	_RELATIVE_BARE)r   s    r   _classify_tokenr     s     ~~  ||u''--| D!! 	E"" 	 %<<Lr   F
force_pathr   r   c                  t          |           }t          j                    }|t          k    r| S |t          k    r| S |t
          k    r| S |t          k    r<t          |           dk    rt          |          | dd         z   nt          |          S |t          k    r| S |t          k    r$t          || z                                            S |r$t          || z                                            S | S )a  Expand a token based on its classification.

    Args:
        token: The token to expand
        cwd: Working directory for resolving relative paths
        force_path: If True, treat BARE tokens as paths (for redirects)

    Returns:
        Expanded token string
    rs   N)r   r   r   r   r   r   r   r   r   r   r   r=   )r   r:   r   kindr   s        r   _expand_tokenr     s     5!!D9;;Dt||yyu}}(+E

Qs4yy59$$CIIEzyC%K((**+++ ,C%K((**+++Lr   c                    t          |           t          k    rOt          j                    }t	          |           dk    rt          |          | dd         z   nt          |          S | S )zExpand only HOME kind tokens (~ and ~/...) at parse time.

    Used for pattern tilde expansion to match settings behavior.
    Other token kinds pass through unchanged.
    rs   N)r   r   r   r   r   r   )r   r   s     r   _expand_home_onlyr     sY     u&&y{{(+E

Qs4yy59$$CIIELr   c                d    d                     d |                                 D                       S )z.Expand tildes in pattern tokens at parse time. c              3  4   K   | ]}t          |          V  d S N)r   )rN   ts     r   	<genexpr>z)_expand_pattern_tildes.<locals>.<genexpr>	  s+      BBQ%a((BBBBBBr   )r   r   r   s    r   r   r     s+    88BB'--//BBBBBBr   c                &    t          | |d          S )z&Normalize a single token in a command.Fr   )r   )r   r:   s     r   _normalize_tokenr     s    6666r   r8   r7   c                F    d                     fd| D                       S )zENormalize paths in command words and join into a string for matching.r   c              3  8   K   | ]}t          |          V  d S r   r   )rN   wr:   s     r   r   z#_normalize_words.<locals>.<genexpr>  s.      <<$Q,,<<<<<<r   )r   )r8   r:   s    `r   _normalize_wordsr     s*    88<<<<e<<<<<<r   c                n    |                                  }d                    fd|D                       S )zNormalize paths in a pattern against cwd.

    Splits pattern on spaces (preserving glob chars), normalizes path-like
    tokens, rejoins. This allows patterns like 'node bin/*' to expand to
    'node /cwd/bin/*'.
    r   c              3  8   K   | ]}t          |          V  d S r   r   )rN   r   r:   s     r   r   z%_normalize_pattern.<locals>.<genexpr>  s.      ==$Q,,======r   )r   r   )r   r:   tokenss    ` r   _normalize_patternr     s9     ]]__F88====f======r   c                L    t          |                     d          |d          S )zCNormalize a redirect target path (strip trailing /, force as path).r   Tr   )r   r   )rV   r:   s     r   _normalize_pathr   !  s#    S))34@@@@r   
re.Patternc                   g }d}t          |           }||k     r| |         }|dk    r||dz   |k     rW| |dz            dk    rH|                    d           |dz  }||k     r&| |         dk    r|                    d           |dz  }nM|                    d           |dz  }n1|d	k    r|                    d
           |dz  }n|dk    r|dz   }||k     r| |         dk    r|dz  }||k     r| |         dk    r|dz  }||k     r#| |         dk    r|dz  }||k     r| |         dk    ||k    r-|                    t          j        |                     |dz  }nz| |dz   |         }|                    d          rd|dd         z   }|                    d| d           |dz   }n,|                    t          j        |                     |dz  }||k     t          j        dd                    |          z   dz             S )zConvert a glob pattern with ** support to a regex.

    ** matches zero or more path components (including /)
    * matches anything except /
    ? matches any single character except /
    [abc] matches character class
    r   *rs   z.*ry   r   z/?z[^/]*?z[^/][!]^Nrr   r   )r   r   reescaper   compiler   )r   regexr   ncr   clss          r   _glob_to_regexr   &  sS    E	AGA
a%%AJ881uqyyWQU^s22T"""Qq55WQZ3..LL&&&FA W%%%Q#XXLL   FAA#XXAA1uus**Q1uus**Qa%%GAJ#--Q a%%GAJ#--AvvRYq\\***Q a!eai(>>#&& (ABB-CZZZZ(((ELL1&&&FAQ a%%R :cBGGENN*S0111r   c                    d|vrt          j         | |          S |dk    rdS 	 t          |          }|                    |           duS # t          j        $ r Y dS w xY w)a  Match text against a glob pattern with ** support.

    For patterns without **, uses fnmatch (faster).
    For patterns with **, converts to regex for proper recursive matching:
    - ** matches zero or more directories
    - foo/**/bar matches foo/bar, foo/x/bar, foo/x/y/bar
    **TNF)fnmatchr   matchr   error)r]   r   r   s      r   _glob_matchr   ]  s|     7tW---$tw''{{4  ,,8   uus   %A	 	AAc                :     t           fddD                       S )z6Check if pattern contains any fnmatch glob characters.c              3      K   | ]}|v V  	d S r   r   )rN   r   r   s     r   r   z"_has_glob_chars.<locals>.<genexpr>r  s'      ++qG|++++++r   z*?[)anyr   s   `r   _has_glob_charsr   p  s&    ++++U++++++r   wordc                    t          | |          }|j                                        D ]\  }}t          ||          }||k    r|c S  | S )z%Resolve command word through aliases.)r   r+   items)r   r   r:   normalized_wordr   r   normalized_sources          r   _resolve_aliasr  u  se    &tS11O&,n&:&:&<&<    "l,\3??/// 0Kr   remoter  Match | Nonec               R   | r(|s&t          | d         ||          }|g| dd         z   }n| }|rd                    |          }nt          ||          }d}|j        D ]}|r|j        }	nt          |j        |          }	d}
|j        s5t          |	          s&|	dz   }t          j        ||          rd}
nC||	k    rd}
n:t          j        ||	          }
|
s#|		                    d          r||	dd         k    }
|
r-t          |j        |j        |j        |j        |j        	          }|S )
z>Match command words against rules. Returns last matching rule.r   rs   Nr   F *Tr   r   r   r   r   )r  r   r   r&   r   r   r    r   r   r   r4   r   r   r   r   )r8   r   r:   r  resolved_firstresolved_wordsnormalized_cmdr   rulenormalized_patternmatchedprefix_patterns               r   _match_wordsr    s     V 'a&#>>()E!""I5 ?.11).#>>F   	G!%!3DL#!F!Fz 	D/2D"E"E 	D/$6N~~>> #555 on6HIIG D1::4@@ D(,>ss,CC 	{j  F Mr   c                    d| vrt          | |          S |                     d          }| d|                             d          }| |d         }|rt          ||          }| d| S | S )zNormalize a redirect pattern, handling ** specially.

    For patterns with **, normalize the prefix before ** and keep the rest.
    For example: 'src/**' -> '/abs/path/to/src/**'
    r   Nr   )r   indexr   )r   r:   idxr   suffixnormalized_prefixs         r   _normalize_redirect_patternr    s     7w,,,
--

CTcT]!!#&&FSTT]F /+FC88#..f...Nr   targetc                    t          | |          }d}|j        D ]T}t          |j        |          }t	          ||          r-t          |j        |j        |j        |j        |j	                  }U|S )z@Match redirect target against rules. Returns last matching rule.Nr	  )
r   r'   r  r   r   r4   r   r   r   r   )r  r   r:   normalized_targetr   r  r  s          r   _match_redirectr    s    '44F% 	 	8sKK(*<== 	{j  F Mr   cmdc               4   g }t          | j        |||          }|r|                    |           |s2| j        D ]*}t	          |||          }|r|                    |           +|sdS |D ]}|j        dk    r|c S |D ]}|j        dk    r|c S |d         S )am  Match command and its redirects against config rules.

    Args:
        cmd: SimpleCommand with words and redirects from parsed bash.
        config: Loaded configuration.
        cwd: Current working directory for path resolution.
        remote: If True, command runs in remote context (container, ssh).
                Skips path expansion since paths are remote, not local.

    Returns:
        Match object for the deciding rule, or None if no rules matched.
        Priority when combining command + redirect matches: deny > ask > allow.
        Returns the first match of the most restrictive decision type.
    r  Nrv   r,   r   )r  r8   r   r9   r  r   )	r  r   r:   r  matches	cmd_matchr  redirect_matchms	            r   match_commandr"    s    " G SYFCCCI "y!!!  /m 	/ 	/F,VVSAAN /~... t   :HHH    :HHH 1:r   c                $    t          | ||          S )a  Match a redirect target against redirect rules.

    This is a convenience function for testing and for cases where you
    need to match a redirect target in isolation. Normally, redirects
    are matched as part of match_command() via SimpleCommand.redirects.

    Args:
        target: Redirect target path.
        config: Loaded configuration.
        cwd: Current working directory for path resolution.

    Returns:
        Match object for the last matching rule, or None if no match.
    )r  )r  r   r:   s      r   match_redirectr$    s     663///r   c                   | r&t          | d         ||          }|g| dd         z   }n| }t          ||          }d}|j        D ]}t          |j        |          }d}	|j        s5t          |          s&|dz   }
t          j        ||
          rd}	nC||k    rd}	n:t          j        ||          }	|	s#|                    d          r||dd         k    }	|	r|j	        |j	        nd}|S )	a  Match command against after rules for PostToolUse feedback.

    Last matching rule wins. Empty string message means silent (no output).

    Args:
        words: Command words (e.g., ["git", "push", "origin", "main"]).
        config: Loaded configuration.
        cwd: Current working directory for path resolution.

    Returns:
        Message string if a rule with message matches, empty string if silent
        rule matches, None if no rule matches.
    r   rs   NFr  Tr  rr   )
r  r   r(   r   r   r    r   r   r   r   )r8   r   r:   r
  r  r  r   r  r  r  r  s              r   match_afterr&    s8     'a&#>>()E!""I5%nc::NF" F F/cBBz 	D/2D"E"E 	D/$6N~~>> #555 on6HIIG D1::4@@ D(,>ss,CC 	F%)\%=T\\2FMr   	tool_namec                    d}|j         D ]I}t          j        | |j                  r-t          |j        |j        |j        |j        |j                  }J|S )aM  Match MCP tool name against mcp rules.

    Simpler than command matching - just fnmatch against tool name.
    Last match wins.

    Args:
        tool_name: MCP tool name (e.g., "mcp__github__get_issue").
        config: Loaded configuration.

    Returns:
        Match object for the last matching rule, or None if no match.
    Nr	  )r)   r   r   r4   r   r   r   r   r'  r   r   r  s       r   	match_mcpr*  >  sf      F   ?9dl33 	{j  F Mr   c                r    d}|j         D ],}t          j        | |j                  r|j        |j        nd}-|S )a  Match MCP tool against after-mcp rules for PostToolUse feedback.

    Last matching rule wins. Empty string message means silent (no output).

    Args:
        tool_name: MCP tool name (e.g., "mcp__github__create_pr").
        config: Loaded configuration.

    Returns:
        Message string if a rule with message matches, empty string if silent
        rule matches, None if no rule matches.
    Nrr   )r*   r   r   r   r)  s       r   match_after_mcpr,  X  sL     F& F F?9dl33 	F%)\%=T\\2FMr   T)frozenc                  ,    e Zd ZU dZded<   dZded<   dS )
_LogConfigzInternal log configuration.r   rV   Fr   fullN)r   r   r   r   r!   r0  r   r   r   r/  r/  o  s2         %%JJJDr   r/  z_LogConfig | None_log_config_log_disabledc                    da | j        dadS 	 | j        j                            dd           n# t
          $ r dada Y dS w xY wt          | j        | j                  adS )zAConfigure logging based on config settings. Call once at startup.FNT)parentsexist_ok)rV   r0  )r2  r/   r1  r@   mkdirr[   r/  r0   )r   s    r   configure_loggingr7  {  s     Mz
t<<<<   
 &*6?CCCKKKs   !1 AAr   r  r   commandc                   ddl }ddlm}m} t          t          rdS | |d}|||d<   |||d<   t          j        r|||d<   |                    |j                                                  |d<   	 t          t          j
        d	          5 }	|	                    |                    |          d
z              ddd           dS # 1 swxY w Y   dS # t          $ r daY dS w xY w)z<Log a decision. No-op if logging not configured or disabled.r   N)datetimetimezone)r   r  r  r   r8  tsa
T)jsonr:  r;  r1  r2  r0  nowutc	isoformatopenrV   writedumpsr[   )
r   r  r  r   r8  r?  r:  r;  entryfs
             r   log_decisionrH    s`    KKK++++++++m08#E#EEf"i #G/"i,,x|,,6688E$K+"C(( 	.AGGDJJu%%,---	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	.   s6   3C ,C9C C

C C
C C#"C#)r:   r   r;   r.   )rD   r#   rE   r#   r;   r#   )r   r#   r   r   r   r   r;   r#   )rV   r   r;   r#   )r:   r   r;   r#   r   )r]   r   r   r   r;   r#   )r   r   r;   r   )r   r   r;   r   )r   r   r;   r   )r   r   r   r   r;   r   )r   r   r;   r   )r   r   r:   r   r   r   r;   r   )r   r   r;   r   )r   r   r:   r   r;   r   )r8   r7   r:   r   r;   r   )r   r   r:   r   r;   r   )rV   r   r:   r   r;   r   )r   r   r;   r   )r]   r   r   r   r;   r   )r   r   r;   r   )r   r   r   r#   r:   r   r;   r   )
r8   r7   r   r#   r:   r   r  r   r;   r  )r  r   r   r#   r:   r   r;   r  )
r  r6   r   r#   r:   r   r  r   r;   r  )r8   r7   r   r#   r:   r   r;   r   )r'  r   r   r#   r;   r  )r'  r   r   r#   r;   r   )r   r#   r;   r   )NNN)r   r   r  r   r  r   r   r   r8  r   r;   r   )Dr   
__future__r   r   rd   r   dataclassesr   r   r   pathlibr   r   r   ra   r>   rg   	Exceptionr   rb   rc   ri   r   r#   r4   r6   rC   rH   rU   r_   rp   r\   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r"  r$  r&  r*  r,  r/  r1  r!   r2  r7  rH  r   r   r   <module>rM     s   $ $ $ " " " " " "  				 				 1 1 1 1 1 1 1 1 1 1       		h) 
	 	 	 	 	) 	 	 	 
	                4         H H H H H H H H$
 
 
 
   &   $0 0 0 0& & & &RM M M M M`       ! ! ! !H5 5 5 5F 		
	   4 @E      @	 	 	 	C C C C
7 7 7 7
= = = =
> > > >A A A A
42 42 42 42n   &, , , ,
    DI- - - - - -`   &   $ FK) ) ) ) ) )X0 0 0 0$* * * *Z   4   . $        "& % % % %    D D D D,       r   