code-graph-builder 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. code_graph_builder/__init__.py +82 -0
  2. code_graph_builder/builder.py +366 -0
  3. code_graph_builder/cgb_cli.py +32 -0
  4. code_graph_builder/cli.py +564 -0
  5. code_graph_builder/commands_cli.py +1288 -0
  6. code_graph_builder/config.py +340 -0
  7. code_graph_builder/constants.py +708 -0
  8. code_graph_builder/embeddings/__init__.py +40 -0
  9. code_graph_builder/embeddings/qwen3_embedder.py +573 -0
  10. code_graph_builder/embeddings/vector_store.py +584 -0
  11. code_graph_builder/examples/__init__.py +0 -0
  12. code_graph_builder/examples/example_configuration.py +276 -0
  13. code_graph_builder/examples/example_kuzu_usage.py +109 -0
  14. code_graph_builder/examples/example_semantic_search_full.py +347 -0
  15. code_graph_builder/examples/generate_wiki.py +915 -0
  16. code_graph_builder/examples/graph_export_example.py +100 -0
  17. code_graph_builder/examples/rag_example.py +206 -0
  18. code_graph_builder/examples/test_cli_demo.py +129 -0
  19. code_graph_builder/examples/test_embedding_api.py +153 -0
  20. code_graph_builder/examples/test_kuzu_local.py +190 -0
  21. code_graph_builder/examples/test_rag_redis.py +390 -0
  22. code_graph_builder/graph_updater.py +605 -0
  23. code_graph_builder/guidance/__init__.py +1 -0
  24. code_graph_builder/guidance/agent.py +123 -0
  25. code_graph_builder/guidance/prompts.py +74 -0
  26. code_graph_builder/guidance/toolset.py +264 -0
  27. code_graph_builder/language_spec.py +536 -0
  28. code_graph_builder/mcp/__init__.py +21 -0
  29. code_graph_builder/mcp/api_doc_generator.py +764 -0
  30. code_graph_builder/mcp/file_editor.py +207 -0
  31. code_graph_builder/mcp/pipeline.py +777 -0
  32. code_graph_builder/mcp/server.py +161 -0
  33. code_graph_builder/mcp/tools.py +1800 -0
  34. code_graph_builder/models.py +115 -0
  35. code_graph_builder/parser_loader.py +344 -0
  36. code_graph_builder/parsers/__init__.py +7 -0
  37. code_graph_builder/parsers/call_processor.py +306 -0
  38. code_graph_builder/parsers/call_resolver.py +139 -0
  39. code_graph_builder/parsers/definition_processor.py +796 -0
  40. code_graph_builder/parsers/factory.py +119 -0
  41. code_graph_builder/parsers/import_processor.py +293 -0
  42. code_graph_builder/parsers/structure_processor.py +145 -0
  43. code_graph_builder/parsers/type_inference.py +143 -0
  44. code_graph_builder/parsers/utils.py +134 -0
  45. code_graph_builder/rag/__init__.py +68 -0
  46. code_graph_builder/rag/camel_agent.py +429 -0
  47. code_graph_builder/rag/client.py +298 -0
  48. code_graph_builder/rag/config.py +239 -0
  49. code_graph_builder/rag/cypher_generator.py +67 -0
  50. code_graph_builder/rag/llm_backend.py +210 -0
  51. code_graph_builder/rag/markdown_generator.py +352 -0
  52. code_graph_builder/rag/prompt_templates.py +440 -0
  53. code_graph_builder/rag/rag_engine.py +640 -0
  54. code_graph_builder/rag/review_report.md +172 -0
  55. code_graph_builder/rag/tests/__init__.py +3 -0
  56. code_graph_builder/rag/tests/test_camel_agent.py +313 -0
  57. code_graph_builder/rag/tests/test_client.py +221 -0
  58. code_graph_builder/rag/tests/test_config.py +177 -0
  59. code_graph_builder/rag/tests/test_markdown_generator.py +240 -0
  60. code_graph_builder/rag/tests/test_prompt_templates.py +160 -0
  61. code_graph_builder/services/__init__.py +39 -0
  62. code_graph_builder/services/graph_service.py +465 -0
  63. code_graph_builder/services/kuzu_service.py +665 -0
  64. code_graph_builder/services/memory_service.py +171 -0
  65. code_graph_builder/settings.py +75 -0
  66. code_graph_builder/tests/ACCEPTANCE_CRITERIA_PHASE2.md +401 -0
  67. code_graph_builder/tests/__init__.py +1 -0
  68. code_graph_builder/tests/run_acceptance_check.py +378 -0
  69. code_graph_builder/tests/test_api_find.py +231 -0
  70. code_graph_builder/tests/test_api_find_integration.py +226 -0
  71. code_graph_builder/tests/test_basic.py +78 -0
  72. code_graph_builder/tests/test_c_api_extraction.py +388 -0
  73. code_graph_builder/tests/test_call_resolution_scenarios.py +504 -0
  74. code_graph_builder/tests/test_embedder.py +411 -0
  75. code_graph_builder/tests/test_integration_semantic.py +434 -0
  76. code_graph_builder/tests/test_mcp_protocol.py +298 -0
  77. code_graph_builder/tests/test_mcp_user_flow.py +190 -0
  78. code_graph_builder/tests/test_rag.py +404 -0
  79. code_graph_builder/tests/test_settings.py +135 -0
  80. code_graph_builder/tests/test_step1_graph_build.py +264 -0
  81. code_graph_builder/tests/test_step2_api_docs.py +323 -0
  82. code_graph_builder/tests/test_step3_embedding.py +278 -0
  83. code_graph_builder/tests/test_vector_store.py +552 -0
  84. code_graph_builder/tools/__init__.py +40 -0
  85. code_graph_builder/tools/graph_query.py +495 -0
  86. code_graph_builder/tools/semantic_search.py +387 -0
  87. code_graph_builder/types.py +333 -0
  88. code_graph_builder/utils/__init__.py +0 -0
  89. code_graph_builder/utils/path_utils.py +30 -0
  90. code_graph_builder-0.2.0.dist-info/METADATA +321 -0
  91. code_graph_builder-0.2.0.dist-info/RECORD +93 -0
  92. code_graph_builder-0.2.0.dist-info/WHEEL +4 -0
  93. code_graph_builder-0.2.0.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,564 @@
1
+ """Command-line interface for Code Graph Builder.
2
+
3
+ Examples:
4
+ # Scan a repository with Kùzu backend
5
+ $ code-graph-builder scan /path/to/repo --backend kuzu --db-path ./graph.db
6
+
7
+ # Scan with specific exclusions
8
+ $ code-graph-builder scan /path/to/repo --exclude tests,docs --exclude-pattern "*.md"
9
+
10
+ # Query the graph
11
+ $ code-graph-builder query "MATCH (f:Function) RETURN f.name LIMIT 5" --db-path ./graph.db
12
+
13
+ # Export to JSON
14
+ $ code-graph-builder export /path/to/repo --output ./output.json
15
+
16
+ # Use configuration file
17
+ $ code-graph-builder scan /path/to/repo --config ./config.yaml
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import argparse
23
+ import json
24
+ import sys
25
+ from pathlib import Path
26
+ from typing import Any
27
+
28
+ from loguru import logger
29
+
30
+ from . import __version__
31
+ from .builder import CodeGraphBuilder
32
+ from .config import (
33
+ KuzuConfig,
34
+ MemgraphConfig,
35
+ MemoryConfig,
36
+ OutputConfig,
37
+ ScanConfig,
38
+ )
39
+
40
+
41
+ def setup_logging(verbose: bool = False) -> None:
42
+ """Setup logging configuration."""
43
+ level = "DEBUG" if verbose else "INFO"
44
+ logger.remove()
45
+ logger.add(
46
+ sys.stderr,
47
+ level=level,
48
+ format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
49
+ )
50
+
51
+
52
+ def load_config_file(config_path: str | Path) -> dict[str, Any]:
53
+ """Load configuration from YAML or JSON file."""
54
+ config_path = Path(config_path)
55
+
56
+ if not config_path.exists():
57
+ raise FileNotFoundError(f"Config file not found: {config_path}")
58
+
59
+ if config_path.suffix in (".yaml", ".yml"):
60
+ try:
61
+ import yaml
62
+
63
+ with open(config_path) as f:
64
+ return yaml.safe_load(f)
65
+ except ImportError:
66
+ raise ImportError("PyYAML is required for YAML config files. Install with: pip install pyyaml")
67
+ elif config_path.suffix == ".json":
68
+ with open(config_path) as f:
69
+ return json.load(f)
70
+ else:
71
+ raise ValueError(f"Unsupported config file format: {config_path.suffix}")
72
+
73
+
74
+ def create_builder_from_args(args: argparse.Namespace) -> CodeGraphBuilder:
75
+ """Create CodeGraphBuilder from command-line arguments."""
76
+ # Load config file if specified
77
+ config = {}
78
+ if hasattr(args, 'config') and args.config:
79
+ config = load_config_file(args.config)
80
+
81
+ # Determine backend
82
+ backend = getattr(args, 'backend', None) or config.get("backend", "kuzu")
83
+
84
+ # Build backend config
85
+ backend_config = config.get("backend_config", {})
86
+ if backend == "kuzu":
87
+ if getattr(args, 'db_path', None):
88
+ backend_config["db_path"] = args.db_path
89
+ if getattr(args, 'batch_size', None):
90
+ backend_config["batch_size"] = args.batch_size
91
+ elif backend == "memgraph":
92
+ if getattr(args, 'host', None):
93
+ backend_config["host"] = args.host
94
+ if getattr(args, 'port', None):
95
+ backend_config["port"] = args.port
96
+ if getattr(args, 'username', None):
97
+ backend_config["username"] = args.username
98
+ if getattr(args, 'password', None):
99
+ backend_config["password"] = args.password
100
+ if getattr(args, 'batch_size', None):
101
+ backend_config["batch_size"] = args.batch_size
102
+
103
+ # Build scan config
104
+ scan_config = config.get("scan_config", {})
105
+ if getattr(args, 'exclude', None):
106
+ # Parse comma-separated exclusions
107
+ exclude_set = set()
108
+ for item in args.exclude:
109
+ exclude_set.update(item.split(","))
110
+ scan_config["exclude_patterns"] = exclude_set
111
+ if getattr(args, 'exclude_pattern', None):
112
+ scan_config.setdefault("exclude_patterns", set()).update(args.exclude_pattern)
113
+ if getattr(args, 'language', None):
114
+ scan_config["include_languages"] = set(args.language.split(","))
115
+ if getattr(args, 'max_file_size', None):
116
+ scan_config["max_file_size"] = args.max_file_size
117
+
118
+ # Create builder
119
+ return CodeGraphBuilder(
120
+ repo_path=args.repo_path,
121
+ backend=backend,
122
+ backend_config=backend_config,
123
+ scan_config=scan_config,
124
+ )
125
+
126
+
127
+ def cmd_scan(args: argparse.Namespace) -> int:
128
+ """Execute the scan command."""
129
+ setup_logging(args.verbose)
130
+
131
+ try:
132
+ logger.info(f"Starting scan of: {args.repo_path}")
133
+ logger.info(f"Backend: {args.backend or 'kuzu'}")
134
+
135
+ builder = create_builder_from_args(args)
136
+
137
+ # Build the graph
138
+ result = builder.build_graph(clean=args.clean)
139
+
140
+ print()
141
+ print("=" * 60)
142
+ print("SCAN COMPLETE")
143
+ print("=" * 60)
144
+ print(f"Repository: {result.project_name}")
145
+ print(f"Nodes created: {result.nodes_created}")
146
+ print(f"Relationships created: {result.relationships_created}")
147
+ print(f"Functions found: {result.functions_found}")
148
+ print(f"Classes found: {result.classes_found}")
149
+ print(f"Files processed: {result.files_processed}")
150
+
151
+ if args.backend == "kuzu" or (not args.backend and args.db_path):
152
+ db_path = args.db_path or f"./{Path(args.repo_path).name}_graph.db"
153
+ print(f"Database saved to: {db_path}")
154
+
155
+ # Export to JSON if requested
156
+ if args.output:
157
+ logger.info(f"Exporting to: {args.output}")
158
+ data = builder.export_graph()
159
+ with open(args.output, "w") as f:
160
+ json.dump(data, f, indent=2, default=str)
161
+ print(f"Exported to: {args.output}")
162
+
163
+ return 0
164
+
165
+ except Exception as e:
166
+ logger.error(f"Scan failed: {e}")
167
+ if args.verbose:
168
+ import traceback
169
+
170
+ traceback.print_exc()
171
+ return 1
172
+
173
+
174
+ def cmd_query(args: argparse.Namespace) -> int:
175
+ """Execute the query command."""
176
+ setup_logging(args.verbose)
177
+
178
+ try:
179
+ # Determine backend from args
180
+ backend = args.backend or "kuzu"
181
+ backend_config = {"db_path": args.db_path} if args.db_path else {}
182
+
183
+ builder = CodeGraphBuilder(
184
+ repo_path=args.repo_path or ".",
185
+ backend=backend,
186
+ backend_config=backend_config,
187
+ )
188
+
189
+ logger.info(f"Executing query: {args.cypher_query}")
190
+ results = builder.query(args.cypher_query)
191
+
192
+ print()
193
+ print("=" * 60)
194
+ print("QUERY RESULTS")
195
+ print("=" * 60)
196
+ print(f"Query: {args.cypher_query}")
197
+ print(f"Results: {len(results)}")
198
+ print()
199
+
200
+ if results:
201
+ # Print as table
202
+ if args.format == "table":
203
+ headers = list(results[0].keys())
204
+ # Calculate column widths
205
+ widths = {h: len(h) for h in headers}
206
+ for row in results:
207
+ for h in headers:
208
+ widths[h] = max(widths[h], len(str(row.get(h, ""))))
209
+
210
+ # Print header
211
+ header_line = " | ".join(h.ljust(widths[h]) for h in headers)
212
+ print(header_line)
213
+ print("-" * len(header_line))
214
+
215
+ # Print rows
216
+ for row in results:
217
+ print(" | ".join(str(row.get(h, "")).ljust(widths[h]) for h in headers))
218
+ else:
219
+ # JSON format
220
+ for i, row in enumerate(results, 1):
221
+ print(f"{i}. {json.dumps(row, default=str)}")
222
+ else:
223
+ print("No results found.")
224
+
225
+ return 0
226
+
227
+ except Exception as e:
228
+ logger.error(f"Query failed: {e}")
229
+ if args.verbose:
230
+ import traceback
231
+
232
+ traceback.print_exc()
233
+ return 1
234
+
235
+
236
+ def cmd_export(args: argparse.Namespace) -> int:
237
+ """Execute the export command."""
238
+ setup_logging(args.verbose)
239
+
240
+ try:
241
+ builder = create_builder_from_args(args)
242
+
243
+ logger.info(f"Exporting graph from: {args.repo_path}")
244
+
245
+ # Build if not already built
246
+ if args.build:
247
+ logger.info("Building graph first...")
248
+ builder.build_graph(clean=args.clean)
249
+
250
+ data = builder.export_graph()
251
+
252
+ output_path = Path(args.output)
253
+ output_path.parent.mkdir(parents=True, exist_ok=True)
254
+
255
+ with open(output_path, "w") as f:
256
+ json.dump(data, f, indent=2, default=str)
257
+
258
+ print()
259
+ print("=" * 60)
260
+ print("EXPORT COMPLETE")
261
+ print("=" * 60)
262
+ print(f"Output: {output_path.absolute()}")
263
+ print(f"Nodes: {len(data.get('nodes', []))}")
264
+ print(f"Relationships: {len(data.get('relationships', []))}")
265
+
266
+ return 0
267
+
268
+ except Exception as e:
269
+ logger.error(f"Export failed: {e}")
270
+ if args.verbose:
271
+ import traceback
272
+
273
+ traceback.print_exc()
274
+ return 1
275
+
276
+
277
+ def cmd_stats(args: argparse.Namespace) -> int:
278
+ """Execute the stats command."""
279
+ setup_logging(args.verbose)
280
+
281
+ try:
282
+ backend = args.backend or "kuzu"
283
+ backend_config = {"db_path": args.db_path} if args.db_path else {}
284
+
285
+ builder = CodeGraphBuilder(
286
+ repo_path=args.repo_path or ".",
287
+ backend=backend,
288
+ backend_config=backend_config,
289
+ )
290
+
291
+ stats = builder.get_statistics()
292
+
293
+ print()
294
+ print("=" * 60)
295
+ print("GRAPH STATISTICS")
296
+ print("=" * 60)
297
+ print(f"Total nodes: {stats.get('total_nodes', 0)}")
298
+ print(f"Total relationships: {stats.get('total_relationships', 0)}")
299
+ print()
300
+
301
+ node_labels = stats.get("node_labels", {})
302
+ if node_labels:
303
+ print("Node types:")
304
+ for label, count in sorted(node_labels.items(), key=lambda x: -x[1]):
305
+ label_str = str(label)
306
+ print(f" {label_str:20s}: {count:5d}")
307
+ print()
308
+
309
+ rel_types = stats.get("relationship_types", {})
310
+ if rel_types:
311
+ print("Relationship types:")
312
+ for rel_type, count in sorted(rel_types.items(), key=lambda x: -x[1]):
313
+ rel_str = str(rel_type)
314
+ print(f" {rel_str:20s}: {count:5d}")
315
+
316
+ return 0
317
+
318
+ except Exception as e:
319
+ logger.error(f"Stats failed: {e}")
320
+ if args.verbose:
321
+ import traceback
322
+
323
+ traceback.print_exc()
324
+ return 1
325
+
326
+
327
+ def main() -> int:
328
+ """Main entry point for CLI."""
329
+ parser = argparse.ArgumentParser(
330
+ prog="code-graph-builder",
331
+ description="Build and query code knowledge graphs",
332
+ formatter_class=argparse.RawDescriptionHelpFormatter,
333
+ epilog="""
334
+ Examples:
335
+ # Scan repository with Kùzu backend
336
+ code-graph-builder scan /path/to/repo --db-path ./graph.db
337
+
338
+ # Scan with exclusions
339
+ code-graph-builder scan /path/to/repo --exclude tests,docs --exclude-pattern "*.md"
340
+
341
+ # Query the graph
342
+ code-graph-builder query "MATCH (f:Function) RETURN f.name LIMIT 5" --db-path ./graph.db
343
+
344
+ # Export to JSON
345
+ code-graph-builder export /path/to/repo --output ./graph.json --build
346
+
347
+ # Show statistics
348
+ code-graph-builder stats --db-path ./graph.db
349
+
350
+ For more information, visit: https://github.com/your-repo/code-graph-builder
351
+ """,
352
+ )
353
+
354
+ parser.add_argument(
355
+ "--version",
356
+ action="version",
357
+ version=f"%(prog)s {__version__}",
358
+ )
359
+ parser.add_argument(
360
+ "-v",
361
+ "--verbose",
362
+ action="store_true",
363
+ help="Enable verbose output",
364
+ )
365
+
366
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
367
+
368
+ # Scan command
369
+ scan_parser = subparsers.add_parser(
370
+ "scan",
371
+ help="Scan a repository and build the knowledge graph",
372
+ description="Scan source code and build a knowledge graph.",
373
+ )
374
+ scan_parser.add_argument(
375
+ "repo_path",
376
+ type=str,
377
+ help="Path to the repository to scan",
378
+ )
379
+ scan_parser.add_argument(
380
+ "--backend",
381
+ choices=["kuzu", "memgraph", "memory"],
382
+ default="kuzu",
383
+ help="Storage backend (default: kuzu)",
384
+ )
385
+ scan_parser.add_argument(
386
+ "--db-path",
387
+ type=str,
388
+ help="Path to store the database (for Kùzu backend)",
389
+ )
390
+ scan_parser.add_argument(
391
+ "--host",
392
+ type=str,
393
+ help="Memgraph host (for Memgraph backend)",
394
+ )
395
+ scan_parser.add_argument(
396
+ "--port",
397
+ type=int,
398
+ help="Memgraph port (for Memgraph backend)",
399
+ )
400
+ scan_parser.add_argument(
401
+ "--username",
402
+ type=str,
403
+ help="Memgraph username",
404
+ )
405
+ scan_parser.add_argument(
406
+ "--password",
407
+ type=str,
408
+ help="Memgraph password",
409
+ )
410
+ scan_parser.add_argument(
411
+ "--batch-size",
412
+ type=int,
413
+ help="Batch size for database writes",
414
+ )
415
+ scan_parser.add_argument(
416
+ "--exclude",
417
+ action="append",
418
+ help="Comma-separated patterns to exclude (can be used multiple times)",
419
+ )
420
+ scan_parser.add_argument(
421
+ "--exclude-pattern",
422
+ action="append",
423
+ help="Additional exclude pattern (can be used multiple times)",
424
+ )
425
+ scan_parser.add_argument(
426
+ "--language",
427
+ type=str,
428
+ help="Comma-separated list of languages to include",
429
+ )
430
+ scan_parser.add_argument(
431
+ "--max-file-size",
432
+ type=int,
433
+ help="Maximum file size in bytes",
434
+ )
435
+ scan_parser.add_argument(
436
+ "--clean",
437
+ action="store_true",
438
+ help="Clean existing database before scanning",
439
+ )
440
+ scan_parser.add_argument(
441
+ "--output",
442
+ "-o",
443
+ type=str,
444
+ help="Export graph to JSON file after scanning",
445
+ )
446
+ scan_parser.add_argument(
447
+ "--config",
448
+ "-c",
449
+ type=str,
450
+ help="Configuration file (YAML or JSON)",
451
+ )
452
+ scan_parser.set_defaults(func=cmd_scan)
453
+
454
+ # Query command
455
+ query_parser = subparsers.add_parser(
456
+ "query",
457
+ help="Execute a Cypher query against the graph",
458
+ description="Query the knowledge graph using Cypher.",
459
+ )
460
+ query_parser.add_argument(
461
+ "cypher_query",
462
+ type=str,
463
+ help="Cypher query to execute",
464
+ )
465
+ query_parser.add_argument(
466
+ "--repo-path",
467
+ type=str,
468
+ help="Path to the repository (for reference)",
469
+ )
470
+ query_parser.add_argument(
471
+ "--backend",
472
+ choices=["kuzu", "memgraph", "memory"],
473
+ default="kuzu",
474
+ help="Storage backend (default: kuzu)",
475
+ )
476
+ query_parser.add_argument(
477
+ "--db-path",
478
+ type=str,
479
+ help="Path to the database (for Kùzu backend)",
480
+ )
481
+ query_parser.add_argument(
482
+ "--format",
483
+ choices=["table", "json"],
484
+ default="table",
485
+ help="Output format (default: table)",
486
+ )
487
+ query_parser.set_defaults(func=cmd_query)
488
+
489
+ # Export command
490
+ export_parser = subparsers.add_parser(
491
+ "export",
492
+ help="Export the graph to JSON",
493
+ description="Export the knowledge graph to a JSON file.",
494
+ )
495
+ export_parser.add_argument(
496
+ "repo_path",
497
+ type=str,
498
+ help="Path to the repository",
499
+ )
500
+ export_parser.add_argument(
501
+ "--output",
502
+ "-o",
503
+ type=str,
504
+ required=True,
505
+ help="Output JSON file path",
506
+ )
507
+ export_parser.add_argument(
508
+ "--backend",
509
+ choices=["kuzu", "memgraph", "memory"],
510
+ default="memory",
511
+ help="Storage backend (default: memory)",
512
+ )
513
+ export_parser.add_argument(
514
+ "--build",
515
+ action="store_true",
516
+ help="Build the graph before exporting",
517
+ )
518
+ export_parser.add_argument(
519
+ "--clean",
520
+ action="store_true",
521
+ help="Clean existing data before building",
522
+ )
523
+ export_parser.add_argument(
524
+ "--exclude",
525
+ action="append",
526
+ help="Patterns to exclude",
527
+ )
528
+ export_parser.set_defaults(func=cmd_export)
529
+
530
+ # Stats command
531
+ stats_parser = subparsers.add_parser(
532
+ "stats",
533
+ help="Show graph statistics",
534
+ description="Display statistics about the knowledge graph.",
535
+ )
536
+ stats_parser.add_argument(
537
+ "--repo-path",
538
+ type=str,
539
+ help="Path to the repository",
540
+ )
541
+ stats_parser.add_argument(
542
+ "--backend",
543
+ choices=["kuzu", "memgraph", "memory"],
544
+ default="kuzu",
545
+ help="Storage backend (default: kuzu)",
546
+ )
547
+ stats_parser.add_argument(
548
+ "--db-path",
549
+ type=str,
550
+ help="Path to the database",
551
+ )
552
+ stats_parser.set_defaults(func=cmd_stats)
553
+
554
+ args = parser.parse_args()
555
+
556
+ if not args.command:
557
+ parser.print_help()
558
+ return 1
559
+
560
+ return args.func(args)
561
+
562
+
563
+ if __name__ == "__main__":
564
+ sys.exit(main())