55import traceback
66from typing import Any , Dict
77
8+ from rich .console import Console
9+ from rich .table import Table
10+ from rich .text import Text
11+ from rich_argparse import RichHelpFormatter
12+
813try :
914 import tomllib
1015except ImportError :
1722from .utils .helpers import is_ipv4
1823
1924
25+ class TableRichHelpFormatter (RichHelpFormatter ):
26+ """
27+ Custom help formatter that displays argument groups with Rich styling.
28+ Uses uppercase group names and custom color scheme.
29+ """
30+
31+ # Customize styles for better appearance
32+ styles = {
33+ ** RichHelpFormatter .styles ,
34+ "argparse.groups" : "bold cyan" ,
35+ "argparse.args" : "green" ,
36+ "argparse.metavar" : "yellow" ,
37+ "argparse.help" : "white" ,
38+ }
39+
40+ # Format group names in uppercase
41+ group_name_formatter = str .upper
42+
43+
44+ class TableHelpAction (argparse .Action ):
45+ """
46+ Custom help action that displays arguments in Rich tables.
47+ """
48+
49+ def __init__ (self , option_strings , dest = argparse .SUPPRESS , default = argparse .SUPPRESS , help = None ):
50+ super ().__init__ (
51+ option_strings = option_strings ,
52+ dest = dest ,
53+ default = default ,
54+ nargs = 0 ,
55+ help = help ,
56+ )
57+
58+ def __call__ (self , parser , namespace , values , option_string = None ):
59+ console = Console ()
60+
61+ # Print description
62+ if parser .description :
63+ console .print (f"\n [bold white]{ parser .description } [/]\n " )
64+
65+ # Print usage
66+ console .print (f"[dim]Usage:[/] [bold]{ parser .prog } [/] [OPTIONS] [TARGETS]\n " )
67+
68+ # Group arguments by their group
69+ for group in parser ._action_groups :
70+ # Skip empty groups
71+ actions = [a for a in group ._group_actions if not isinstance (a , argparse ._HelpAction ) and not isinstance (a , TableHelpAction )]
72+ if not actions :
73+ continue
74+
75+ # Print title
76+ console .print (f"[bold cyan]{ group .title .upper ()} [/]" )
77+
78+ # Print description if present
79+ if group .description :
80+ console .print (f"[dim]{ group .description } [/]" )
81+
82+ # Create table for this group
83+ table = Table (
84+ border_style = "dim" ,
85+ show_header = True ,
86+ header_style = "bold white" ,
87+ padding = (0 , 1 ),
88+ expand = False ,
89+ )
90+
91+ table .add_column ("Option" , style = "green" , no_wrap = True )
92+ table .add_column ("Description" , style = "white" )
93+
94+ for action in actions :
95+ # Build option string
96+ opts = ", " .join (action .option_strings ) if action .option_strings else action .dest
97+
98+ # Add metavar if present
99+ if action .metavar :
100+ opts += f" [yellow]{ action .metavar } [/]"
101+ elif action .type and action .type != bool :
102+ opts += f" [yellow]{ action .dest .upper ()} [/]"
103+
104+ # Get help text - don't add default if already mentioned in help
105+ help_text = action .help or ""
106+ if action .default and action .default != argparse .SUPPRESS and action .default is not None :
107+ if action .default is not True and action .default is not False :
108+ # Only add default if not already in help text
109+ if "default:" not in help_text .lower ():
110+ help_text += f" [dim](default: { action .default } )[/]"
111+
112+ table .add_row (opts , help_text )
113+
114+ console .print (table )
115+ console .print ()
116+
117+ parser .exit ()
118+
119+
20120class OnceOnly (argparse .Action ):
21121 """
22122 Custom argparse Action to prevent arguments from being specified multiple times.
@@ -226,8 +326,14 @@ def load_config() -> Dict[str, Any]:
226326
227327def build_parser () -> argparse .ArgumentParser :
228328 ap = argparse .ArgumentParser (
229- prog = "taskhound" , description = "TaskHound - Scheduled Task privilege checker with optional High Value enrichment"
230- )
329+ prog = "taskhound" ,
330+ description = "Windows Privileged Scheduled Task Discovery Tool for fun and profit." ,
331+ formatter_class = TableRichHelpFormatter ,
332+ add_help = False , # Disable default help
333+ )
334+ # Add custom table-based help
335+ ap .add_argument ("-h" , "--help" , action = TableHelpAction , help = "Show this help message" )
336+
231337 # Authentication options
232338 auth = ap .add_argument_group ("Authentication options" )
233339 auth .add_argument ("-u" , "--username" , action = OnceOnly , help = "Username (required for online mode)" )
@@ -248,8 +354,8 @@ def build_parser() -> argparse.ArgumentParser:
248354 target .add_argument (
249355 "--timeout" ,
250356 type = int ,
251- default = 60 ,
252- help = "Connection timeout in seconds (default: 60 ). Lower this to speed up scans of dead hosts." ,
357+ default = 5 ,
358+ help = "Connection timeout in seconds (default: 5 ). Lower values speed up scans with unreachable hosts." ,
253359 )
254360 target .add_argument (
255361 "--threads" ,
0 commit comments