@@ -359,7 +359,7 @@ pub fn run_mcp_list_command(json: bool) -> Result<()> {
359359}
360360
361361use crate :: cli:: args:: HooksCommand ;
362- use crate :: hooks:: config:: { HookEvent , HookHandlerConfig } ;
362+ use crate :: hooks:: config:: { HookEvent , HookHandlerConfig , HttpHandlerConfig } ;
363363
364364pub async fn run_hooks_command ( args : HooksCommand ) -> Result < ( ) > {
365365 match args {
@@ -402,11 +402,8 @@ fn parse_hook_event(event_str: &str) -> Result<HookEvent> {
402402 . ok_or_else ( || anyhow:: anyhow!( "Invalid event name '{}'. Valid events: pre_tool_use, post_tool_use, pre_session, post_session, error, custom:<name>" , event_str) )
403403}
404404
405- fn parse_handler_type ( handler_type : & str ) -> Result < HookHandlerConfig > {
406- match handler_type. to_ascii_lowercase ( ) . as_str ( ) {
407- "command" => Ok ( HookHandlerConfig :: default ( ) ) ,
408- _ => anyhow:: bail!( "Invalid handler type '{}'. Valid types: command" , handler_type) ,
409- }
405+ fn parse_handler_type ( _handler_type : & str ) -> Result < HookHandlerConfig > {
406+ anyhow:: bail!( "parse_handler_type is deprecated; use handler type specific parsing in run_hooks_add" )
410407}
411408
412409async fn run_hooks_list ( event : Option < String > ) -> Result < ( ) > {
@@ -433,10 +430,20 @@ async fn run_hooks_list(event: Option<String>) -> Result<()> {
433430 printed_header = true ;
434431 }
435432
436- println ! (
437- "[{}] command=\" {}\" timeout={:?}" ,
438- event_name, handler. command, handler. timeout_secs
439- ) ;
433+ match handler {
434+ HookHandlerConfig :: Command ( cmd) => {
435+ println ! (
436+ "[{}] type=command command=\" {}\" timeout={:?}" ,
437+ event_name, cmd. command, cmd. timeout_secs
438+ ) ;
439+ }
440+ HookHandlerConfig :: Http ( http) => {
441+ println ! (
442+ "[{}] type=http url=\" {}\" method={} timeout={:?}" ,
443+ event_name, http. url, http. method, http. timeout_secs
444+ ) ;
445+ }
446+ }
440447 }
441448
442449 if !printed_header && event. is_some ( ) {
@@ -447,36 +454,57 @@ async fn run_hooks_list(event: Option<String>) -> Result<()> {
447454}
448455
449456async fn run_hooks_add ( event : String , handler_type : String , config_json : String ) -> Result < ( ) > {
450- // Parse event
451457 let hook_event = parse_hook_event ( & event) ?;
452458 let event_key = match & hook_event {
453459 HookEvent :: Custom ( name) => format ! ( "custom:{}" , name) ,
454460 other => format ! ( "{:?}" , other) . to_lowercase ( ) ,
455461 } ;
456462
457- // Parse handler type
458- let mut handler = parse_handler_type ( & handler_type) ?;
459-
460- // Parse config JSON to extract command
461463 let config: serde_json:: Value = serde_json:: from_str ( & config_json)
462464 . with_context ( || format ! ( "Failed to parse config JSON: {}" , config_json) ) ?;
463465
464- // Extract command (required)
465- let command = config
466- . get ( "command" )
467- . and_then ( |v| v. as_str ( ) )
468- . ok_or_else ( || anyhow:: anyhow!( "Config JSON must include 'command' field" ) ) ?;
469- handler. command = command. to_string ( ) ;
470-
471- // Extract optional args
472- if let Some ( args) = config. get ( "args" ) . and_then ( |v| v. as_array ( ) ) {
473- handler. args = args
474- . iter ( )
475- . filter_map ( |v| v. as_str ( ) . map ( String :: from) )
476- . collect ( ) ;
477- }
466+ let handler = match handler_type. to_ascii_lowercase ( ) . as_str ( ) {
467+ "command" => {
468+ let command = config
469+ . get ( "command" )
470+ . and_then ( |v| v. as_str ( ) )
471+ . ok_or_else ( || anyhow:: anyhow!( "Config JSON must include 'command' field" ) ) ?;
472+ let mut handler = crate :: hooks:: config:: CommandHandlerConfig :: default ( ) ;
473+ handler. command = command. to_string ( ) ;
474+ if let Some ( args) = config. get ( "args" ) . and_then ( |v| v. as_array ( ) ) {
475+ handler. args = args
476+ . iter ( )
477+ . filter_map ( |v| v. as_str ( ) . map ( String :: from) )
478+ . collect ( ) ;
479+ }
480+ HookHandlerConfig :: Command ( handler)
481+ }
482+ "http" => {
483+ let url = config
484+ . get ( "url" )
485+ . and_then ( |v| v. as_str ( ) )
486+ . ok_or_else ( || anyhow:: anyhow!( "Config JSON must include 'url' field" ) ) ?;
487+ let method = config
488+ . get ( "method" )
489+ . and_then ( |v| v. as_str ( ) )
490+ . unwrap_or ( "GET" ) ;
491+ let mut handler = HttpHandlerConfig :: default ( ) ;
492+ handler. url = url. to_string ( ) ;
493+ handler. method = method. to_string ( ) ;
494+ if let Some ( headers) = config. get ( "headers" ) . and_then ( |v| v. as_object ( ) ) {
495+ handler. headers = headers
496+ . iter ( )
497+ . filter_map ( |( k, v) | v. as_str ( ) . map ( |s| ( k. clone ( ) , s. to_string ( ) ) ) )
498+ . collect ( ) ;
499+ }
500+ if let Some ( body) = config. get ( "body" ) {
501+ handler. body = Some ( body. clone ( ) ) ;
502+ }
503+ HookHandlerConfig :: Http ( handler)
504+ }
505+ _ => anyhow:: bail!( "Invalid handler type '{}'. Valid types: command, http" , handler_type) ,
506+ } ;
478507
479- // Load existing config and add the new hook
480508 let mut config = load_user_hooks_config ( ) ?;
481509 config. events . insert ( event_key. clone ( ) , handler) ;
482510 save_user_hooks_config ( & config) ?;
0 commit comments