Coverage for .claude/hooks/moai/lib/notification.py: 0.00%

83 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2025-11-19 08:00 +0900

1#!/usr/bin/env python3 

2"""Notification and control handlers 

3 

4Notification, Stop, SubagentStop event handling 

5""" 

6 

7import json 

8from datetime import datetime 

9from pathlib import Path 

10from typing import Any, Dict 

11 

12from lib import HookPayload, HookResult 

13 

14 

15def _get_command_state_file(cwd: str) -> Path: 

16 """Get the path to command state tracking file""" 

17 state_dir = Path(cwd) / ".moai" / "memory" 

18 state_dir.mkdir(parents=True, exist_ok=True) 

19 return state_dir / "command-execution-state.json" 

20 

21 

22def _load_command_state(cwd: str) -> Dict[str, Any]: 

23 """Load current command execution state""" 

24 try: 

25 state_file = _get_command_state_file(cwd) 

26 if state_file.exists(): 

27 with open(state_file, "r", encoding="utf-8") as f: 

28 return json.load(f) 

29 except Exception: 

30 pass 

31 return {"last_command": None, "last_timestamp": None, "is_running": False} 

32 

33 

34def _save_command_state(cwd: str, state: Dict[str, Any]) -> None: 

35 """Save command execution state""" 

36 try: 

37 state_file = _get_command_state_file(cwd) 

38 with open(state_file, "w", encoding="utf-8") as f: 

39 json.dump(state, f, indent=2) 

40 except Exception: 

41 pass 

42 

43 

44def _is_duplicate_command(current_cmd: str, last_cmd: str, last_timestamp: str) -> bool: 

45 """Check if current command is a duplicate of the last one within 3 seconds""" 

46 if not last_cmd or not last_timestamp or current_cmd != last_cmd: 

47 return False 

48 

49 try: 

50 last_time = datetime.fromisoformat(last_timestamp) 

51 current_time = datetime.now() 

52 time_diff = (current_time - last_time).total_seconds() 

53 # Consider it a duplicate if same command within 3 seconds 

54 return time_diff < 3 

55 except Exception: 

56 return False 

57 

58 

59def handle_notification(payload: HookPayload) -> HookResult: # type: ignore[return] 

60 """Notification event handler 

61 

62 Detects and warns about duplicate command executions 

63 (When the same /alfred: command is triggered multiple times within 3 seconds) 

64 """ 

65 cwd = payload.get("cwd", ".") 

66 notification = payload.get("notification", {}) 

67 

68 # Extract command information from notification 

69 current_cmd = None 

70 if isinstance(notification, dict): 

71 # Check if notification contains command information 

72 text = notification.get("text", "") or str(notification) 

73 if "/alfred:" in text: 

74 # Extract /alfred: command 

75 import re 

76 

77 match = re.search(r"/alfred:\S+", text) 

78 if match: 

79 current_cmd = match.group() 

80 

81 if not current_cmd: 

82 return HookResult() 

83 

84 # Load current state 

85 state = _load_command_state(cwd) 

86 last_cmd = state.get("last_command") 

87 last_timestamp = state.get("last_timestamp") 

88 

89 # Check for duplicate 

90 if _is_duplicate_command(current_cmd, last_cmd, last_timestamp): 

91 warning_msg = ( 

92 f"⚠️ Duplicate command detected: '{current_cmd}' " 

93 f"is running multiple times within 3 seconds.\n" 

94 f"This may indicate a system issue. Check logs in `.moai/logs/command-invocations.log`" 

95 ) 

96 

97 # Update state - mark as duplicate detected 

98 state["duplicate_detected"] = True 

99 state["duplicate_command"] = current_cmd 

100 state["duplicate_timestamp"] = datetime.now().isoformat() 

101 _save_command_state(cwd, state) 

102 

103 return HookResult(system_message=warning_msg, continue_execution=True) 

104 

105 # Update state with current command 

106 state["last_command"] = current_cmd 

107 state["last_timestamp"] = datetime.now().isoformat() 

108 state["is_running"] = True 

109 state["duplicate_detected"] = False 

110 _save_command_state(cwd, state) 

111 

112 return HookResult() 

113 

114 

115def handle_stop(payload: HookPayload) -> HookResult: 

116 """Stop event handler 

117 

118 Marks command execution as complete 

119 """ 

120 cwd = payload.get("cwd", ".") 

121 state = _load_command_state(cwd) 

122 state["is_running"] = False 

123 state["last_timestamp"] = datetime.now().isoformat() 

124 _save_command_state(cwd, state) 

125 

126 return HookResult() 

127 

128 

129def handle_subagent_stop(payload: HookPayload) -> HookResult: 

130 """SubagentStop event handler 

131 

132 Records when a sub-agent finishes execution 

133 """ 

134 cwd = payload.get("cwd", ".") 

135 

136 # Extract subagent name if available 

137 subagent_name = ( 

138 payload.get("subagent", {}).get("name") 

139 if isinstance(payload.get("subagent"), dict) 

140 else None 

141 ) 

142 

143 try: 

144 state_file = _get_command_state_file(cwd).parent / "subagent-execution.log" 

145 timestamp = datetime.now().isoformat() 

146 

147 with open(state_file, "a", encoding="utf-8") as f: 

148 f.write(f"{timestamp} | Subagent Stop | {subagent_name}\n") 

149 except Exception: 

150 pass 

151 

152 return HookResult() 

153 

154 

155__all__ = ["handle_notification", "handle_stop", "handle_subagent_stop"]