@@ -119,6 +119,42 @@ def _resolve_whitelist_file(whitelist_file_arg: str | None, root_dir: Path) -> P
119119 return resolved
120120
121121
122+ def _build_graph_parsed_args (args : argparse .Namespace ) -> ParsedArgs :
123+ root_dir = _resolve_root_dir (args .directory )
124+ output_file_path = Path (args .output_file ).resolve () if args .output_file else None
125+ ignore_file = _resolve_ignore_file (args .ignore , root_dir )
126+ whitelist_file = _resolve_whitelist_file (args .whitelist , root_dir )
127+ verbosity = "error" if args .quiet else args .log_level
128+ edge_types = [t .strip () for t in args .edge_types .split ("," )] if args .edge_types else None
129+
130+ return ParsedArgs (
131+ root_dir = root_dir ,
132+ ignore_file = ignore_file ,
133+ whitelist_file = whitelist_file ,
134+ output_file = output_file_path ,
135+ no_default_ignores = args .no_default_ignores ,
136+ verbosity = verbosity ,
137+ output_format = "yaml" ,
138+ max_depth = None ,
139+ no_content = False ,
140+ max_file_bytes = None ,
141+ copy = args .copy ,
142+ force_stdout = False ,
143+ quiet = args .quiet ,
144+ command = "graph" ,
145+ format = args .format ,
146+ summary = args .summary ,
147+ level = args .level ,
148+ edge_types = edge_types ,
149+ mermaid = args .mermaid ,
150+ cycles = args .cycles ,
151+ hotspots = args .hotspots ,
152+ metrics = args .metrics ,
153+ impact = args .impact ,
154+ blast_radius = args .blast_radius ,
155+ )
156+
157+
122158def _warn_diff_only_flags (args : argparse .Namespace ) -> None :
123159 if args .diff_range :
124160 return
@@ -156,6 +192,17 @@ class ParsedArgs:
156192 alpha : float = 0.60
157193 tau : float = 0.08
158194 full_diff : bool = False
195+ command : str | None = None
196+ format : str = "json"
197+ summary : bool = False
198+ level : str = "fragment"
199+ edge_types : list [str ] | None = None
200+ mermaid : bool = False
201+ cycles : bool = False
202+ hotspots : int | None = None
203+ metrics : bool = False
204+ impact : str | None = None
205+ blast_radius : str | None = None
159206
160207
161208DEFAULT_IGNORES_HELP = """
@@ -200,12 +247,72 @@ class ParsedArgs:
200247"""
201248
202249
203- def parse_args () -> ParsedArgs :
250+ def _build_graph_parser () -> argparse .ArgumentParser :
251+ graph_parser = argparse .ArgumentParser (
252+ prog = "treemapper graph" ,
253+ description = "Build and analyze the project dependency graph" ,
254+ formatter_class = argparse .RawDescriptionHelpFormatter ,
255+ )
256+ graph_parser .add_argument ("directory" , nargs = "?" , default = "." , help = "The directory to analyze" )
257+ graph_parser .add_argument (
258+ "-f" ,
259+ "--format" ,
260+ choices = ["json" , "graphml" ],
261+ default = "json" ,
262+ help = "Graph output format (default: json)" ,
263+ )
264+ graph_parser .add_argument ("--summary" , action = "store_true" , help = "Print graph summary statistics" )
265+ graph_parser .add_argument (
266+ "--level" ,
267+ choices = ["fragment" , "file" , "directory" ],
268+ default = "fragment" ,
269+ help = "Granularity level for graph operations (default: fragment)" ,
270+ )
271+ graph_parser .add_argument (
272+ "--edge-types" ,
273+ default = None ,
274+ help = "Comma-separated edge types to include (e.g., semantic,config)" ,
275+ )
276+ graph_parser .add_argument ("--mermaid" , action = "store_true" , help = "Output graph as Mermaid diagram" )
277+ graph_parser .add_argument ("--cycles" , action = "store_true" , help = "Detect dependency cycles" )
278+ graph_parser .add_argument (
279+ "--hotspots" , type = int , nargs = "?" , const = 10 , default = None , metavar = "N" , help = "Show top N hotspots (default: 10)"
280+ )
281+ graph_parser .add_argument ("--metrics" , action = "store_true" , help = "Show coupling/cohesion metrics per module" )
282+ graph_parser .add_argument ("--impact" , default = None , metavar = "FILE" , help = "Show impact subgraph for a file" )
283+ graph_parser .add_argument ("--blast-radius" , default = None , metavar = "FILE" , help = "Estimate blast radius for a file" )
284+ graph_parser .add_argument ("-o" , "--output-file" , default = None , help = "Write output to FILE" )
285+ graph_parser .add_argument ("-i" , "--ignore" , default = None , help = "Path to custom ignore file" )
286+ graph_parser .add_argument ("-w" , "--whitelist" , default = None , help = "Path to whitelist file" )
287+ graph_parser .add_argument (
288+ "--no-default-ignores" ,
289+ action = "store_true" ,
290+ help = "Disable built-in ignore patterns" ,
291+ )
292+ graph_parser .add_argument ("-c" , "--copy" , action = "store_true" , help = "Copy to clipboard" )
293+ graph_parser .add_argument (
294+ "-q" ,
295+ "--quiet" ,
296+ action = "store_true" ,
297+ help = "Suppress all non-error output" ,
298+ )
299+ graph_parser .add_argument (
300+ "--log-level" ,
301+ choices = ["error" , "warning" , "info" , "debug" ],
302+ default = "error" ,
303+ help = "Log level (default: error)" ,
304+ )
305+ return graph_parser
306+
307+
308+ def _build_main_parser () -> argparse .ArgumentParser :
204309 parser = argparse .ArgumentParser (
205310 prog = "treemapper" ,
206311 description = (
207312 "Generate a structured representation of a directory tree (YAML, JSON, text, or Markdown). "
208- "Supports diff context mode (--diff) for intelligent code change analysis."
313+ "Supports diff context mode (--diff) for intelligent code change analysis.\n \n "
314+ "Subcommands:\n "
315+ " graph Build and analyze the project dependency graph"
209316 ),
210317 epilog = DEFAULT_IGNORES_HELP ,
211318 formatter_class = argparse .RawDescriptionHelpFormatter ,
@@ -299,8 +406,19 @@ def parse_args() -> ParsedArgs:
299406 action = "store_true" ,
300407 help = "Include all changed code (skip smart selection algorithm)" ,
301408 )
409+ return parser
410+
411+
412+ def parse_args (argv : list [str ] | None = None ) -> ParsedArgs :
413+ raw_args = sys .argv [1 :] if argv is None else argv
414+
415+ if raw_args and raw_args [0 ] == "graph" :
416+ graph_parser = _build_graph_parser ()
417+ args = graph_parser .parse_args (raw_args [1 :])
418+ return _build_graph_parsed_args (args )
302419
303- args = parser .parse_args ()
420+ parser = _build_main_parser ()
421+ args = parser .parse_args (raw_args )
304422
305423 _validate_max_depth (args .max_depth )
306424 max_file_bytes = _validate_max_file_bytes (args .max_file_bytes , args .no_file_size_limit )
0 commit comments