@@ -776,32 +776,14 @@ impl Agents {
776776
777777 /// Returns a label to describe the permission status for a given tool.
778778 pub fn display_label ( & self , tool_name : & str , origin : & ToolOrigin ) -> String {
779- use crate :: util:: pattern_matching :: matches_any_pattern ;
779+ use crate :: util:: tool_permission_checker :: is_tool_in_allowlist ;
780780
781781 let tool_trusted = self . get_active ( ) . is_some_and ( |a| {
782- if matches ! ( origin, & ToolOrigin :: Native ) {
783- return matches_any_pattern ( & a. allowed_tools , tool_name) ;
784- }
785-
786- a. allowed_tools . iter ( ) . any ( |name| {
787- name. strip_prefix ( "@" ) . is_some_and ( |remainder| {
788- remainder
789- . split_once ( MCP_SERVER_TOOL_DELIMITER )
790- . is_some_and ( |( _left, right) | right == tool_name)
791- || remainder == <ToolOrigin as Borrow < str > >:: borrow ( origin)
792- } ) || {
793- if let Some ( server_name) = name. strip_prefix ( "@" ) . and_then ( |s| s. split ( '/' ) . next ( ) ) {
794- if server_name == <ToolOrigin as Borrow < str > >:: borrow ( origin) {
795- let tool_pattern = format ! ( "@{}/{}" , server_name, tool_name) ;
796- matches_any_pattern ( & a. allowed_tools , & tool_pattern)
797- } else {
798- false
799- }
800- } else {
801- false
802- }
803- }
804- } )
782+ let server_name = match origin {
783+ ToolOrigin :: Native => None ,
784+ ToolOrigin :: McpServer ( _) => Some ( <ToolOrigin as Borrow < str > >:: borrow ( origin) ) ,
785+ } ;
786+ is_tool_in_allowlist ( & a. allowed_tools , tool_name, server_name)
805787 } ) ;
806788
807789 if tool_trusted || self . trust_all_tools {
@@ -818,9 +800,9 @@ impl Agents {
818800 "fs_read" => "trust working directory" . dark_grey ( ) ,
819801 "fs_write" => "not trusted" . dark_grey ( ) ,
820802 #[ cfg( not( windows) ) ]
821- "execute_bash" => "trust read-only commands " . dark_grey ( ) ,
803+ "execute_bash" => "not trusted " . dark_grey ( ) ,
822804 #[ cfg( windows) ]
823- "execute_cmd" => "trust read-only commands " . dark_grey ( ) ,
805+ "execute_cmd" => "not trusted " . dark_grey ( ) ,
824806 "use_aws" => "trust read-only commands" . dark_grey ( ) ,
825807 "report_issue" => "trusted" . dark_green ( ) . bold ( ) ,
826808 "introspect" => "trusted" . dark_green ( ) . bold ( ) ,
@@ -959,6 +941,7 @@ mod tests {
959941 use serde_json:: json;
960942
961943 use super :: * ;
944+ use crate :: cli:: agent:: hook:: Source ;
962945 const INPUT : & str = r#"
963946 {
964947 "name": "some_agent",
@@ -968,21 +951,21 @@ mod tests {
968951 "fetch": { "command": "fetch3.1", "args": [] },
969952 "git": { "command": "git-mcp", "args": [] }
970953 },
971- "tools": [
954+ "tools": [
972955 "@git"
973956 ],
974957 "toolAliases": {
975958 "@gits/some_tool": "some_tool2"
976959 },
977- "allowedTools": [
978- "fs_read",
960+ "allowedTools": [
961+ "fs_read",
979962 "@fetch",
980963 "@gits/git_status"
981964 ],
982- "resources": [
965+ "resources": [
983966 "file://~/my-genai-prompts/unittest.md"
984967 ],
985- "toolsSettings": {
968+ "toolsSettings": {
986969 "fs_write": { "allowedPaths": ["~/**"] },
987970 "@git/git_status": { "git_user": "$GIT_USER" }
988971 }
@@ -1188,8 +1171,8 @@ mod tests {
11881171 let execute_name = if cfg ! ( windows) { "execute_cmd" } else { "execute_bash" } ;
11891172 let execute_bash_label = agents. display_label ( execute_name, & ToolOrigin :: Native ) ;
11901173 assert ! (
1191- execute_bash_label. contains( "read-only " ) ,
1192- "execute_bash should show read-only by default, instead found: {}" ,
1174+ execute_bash_label. contains( "not trusted " ) ,
1175+ "execute_bash should not be trusted by default, instead found: {}" ,
11931176 execute_bash_label
11941177 ) ;
11951178 }
@@ -1353,4 +1336,70 @@ mod tests {
13531336
13541337 assert_eq ! ( agents. get_active( ) . and_then( |a| a. model. as_ref( ) ) , None ) ;
13551338 }
1339+
1340+ #[ test]
1341+ fn test_agent_with_hooks ( ) {
1342+ let agent_json = json ! ( {
1343+ "name" : "test-agent" ,
1344+ "hooks" : {
1345+ "agentSpawn" : [
1346+ {
1347+ "command" : "git status"
1348+ }
1349+ ] ,
1350+ "preToolUse" : [
1351+ {
1352+ "matcher" : "fs_write" ,
1353+ "command" : "validate-tool.sh"
1354+ } ,
1355+ {
1356+ "matcher" : "fs_read" ,
1357+ "command" : "enforce-tdd.sh"
1358+ }
1359+ ] ,
1360+ "postToolUse" : [
1361+ {
1362+ "matcher" : "fs_write" ,
1363+ "command" : "format-python.sh"
1364+ }
1365+ ]
1366+ }
1367+ } ) ;
1368+
1369+ let agent: Agent = serde_json:: from_value ( agent_json) . expect ( "Failed to deserialize agent" ) ;
1370+
1371+ // Verify agent name
1372+ assert_eq ! ( agent. name, "test-agent" ) ;
1373+
1374+ // Verify agentSpawn hook
1375+ assert ! ( agent. hooks. contains_key( & HookTrigger :: AgentSpawn ) ) ;
1376+ let agent_spawn_hooks = & agent. hooks [ & HookTrigger :: AgentSpawn ] ;
1377+ assert_eq ! ( agent_spawn_hooks. len( ) , 1 ) ;
1378+ assert_eq ! ( agent_spawn_hooks[ 0 ] . command, "git status" ) ;
1379+ assert_eq ! ( agent_spawn_hooks[ 0 ] . matcher, None ) ;
1380+
1381+ // Verify preToolUse hooks
1382+ assert ! ( agent. hooks. contains_key( & HookTrigger :: PreToolUse ) ) ;
1383+ let pre_tool_hooks = & agent. hooks [ & HookTrigger :: PreToolUse ] ;
1384+ assert_eq ! ( pre_tool_hooks. len( ) , 2 ) ;
1385+
1386+ assert_eq ! ( pre_tool_hooks[ 0 ] . command, "validate-tool.sh" ) ;
1387+ assert_eq ! ( pre_tool_hooks[ 0 ] . matcher, Some ( "fs_write" . to_string( ) ) ) ;
1388+
1389+ assert_eq ! ( pre_tool_hooks[ 1 ] . command, "enforce-tdd.sh" ) ;
1390+ assert_eq ! ( pre_tool_hooks[ 1 ] . matcher, Some ( "fs_read" . to_string( ) ) ) ;
1391+
1392+ // Verify postToolUse hooks
1393+ assert ! ( agent. hooks. contains_key( & HookTrigger :: PostToolUse ) ) ;
1394+
1395+ // Verify default values are set correctly
1396+ for hooks in agent. hooks . values ( ) {
1397+ for hook in hooks {
1398+ assert_eq ! ( hook. timeout_ms, 30_000 ) ;
1399+ assert_eq ! ( hook. max_output_size, 10_240 ) ;
1400+ assert_eq ! ( hook. cache_ttl_seconds, 0 ) ;
1401+ assert_eq ! ( hook. source, Source :: Agent ) ;
1402+ }
1403+ }
1404+ }
13561405}
0 commit comments