codegraph-ai 0.2.2__tar.gz → 0.3.0__tar.gz

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 (61) hide show
  1. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/PKG-INFO +4 -1
  2. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/cli.py +218 -0
  3. codegraph_ai-0.3.0/codegraph/graph_export.py +730 -0
  4. codegraph_ai-0.3.0/codegraph/guided_explorer/__init__.py +54 -0
  5. codegraph_ai-0.3.0/codegraph/guided_explorer/interactive_explorer.py +515 -0
  6. codegraph_ai-0.3.0/codegraph/guided_explorer/pattern_detector.py +758 -0
  7. codegraph_ai-0.3.0/codegraph/guided_explorer/pattern_severity.yml +30 -0
  8. codegraph_ai-0.3.0/codegraph/guided_explorer/pr_explorer.py +464 -0
  9. codegraph_ai-0.3.0/codegraph/guided_explorer/pr_question_generator.py +428 -0
  10. codegraph_ai-0.3.0/codegraph/guided_explorer/pr_question_templates.yml +94 -0
  11. codegraph_ai-0.3.0/codegraph/guided_explorer/question_generator.py +503 -0
  12. codegraph_ai-0.3.0/codegraph/guided_explorer/question_templates.yml +166 -0
  13. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/models.py +94 -0
  14. codegraph_ai-0.3.0/codegraph/pr_analysis.py +1431 -0
  15. codegraph_ai-0.3.0/codegraph/pr_api.py +296 -0
  16. codegraph_ai-0.3.0/codegraph/pr_labeler.py +616 -0
  17. codegraph_ai-0.3.0/codegraph/pr_locator.py +764 -0
  18. codegraph_ai-0.3.0/codegraph/pr_review.py +672 -0
  19. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph_ai.egg-info/PKG-INFO +4 -1
  20. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph_ai.egg-info/SOURCES.txt +17 -0
  21. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph_ai.egg-info/requires.txt +3 -0
  22. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/pyproject.toml +7 -1
  23. codegraph_ai-0.3.0/tests/test_guided_explorer.py +413 -0
  24. codegraph_ai-0.3.0/tests/test_pr_review.py +414 -0
  25. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/README.md +0 -0
  26. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/__init__.py +0 -0
  27. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/__main__.py +0 -0
  28. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/adapters/__init__.py +0 -0
  29. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/adapters/base.py +0 -0
  30. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/adapters/c_adapter.py +0 -0
  31. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/adapters/java_adapter.py +0 -0
  32. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/adapters/js_adapter.py +0 -0
  33. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/adapters/python_adapter.py +0 -0
  34. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/analyzer.py +0 -0
  35. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/bug_locator.py +0 -0
  36. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/bug_parser.py +0 -0
  37. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/core.py +0 -0
  38. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/github_client.py +0 -0
  39. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/issue_cache.py +0 -0
  40. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/issue_fetcher.py +0 -0
  41. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/mcp_server.py +0 -0
  42. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph/qa.py +0 -0
  43. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph_ai.egg-info/dependency_links.txt +0 -0
  44. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph_ai.egg-info/entry_points.txt +0 -0
  45. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/codegraph_ai.egg-info/top_level.txt +0 -0
  46. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/setup.cfg +0 -0
  47. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_adapters.py +0 -0
  48. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_advanced.py +0 -0
  49. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_bug_locator.py +0 -0
  50. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_bug_parser.py +0 -0
  51. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_core_schema.py +0 -0
  52. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_cross_locate.py +0 -0
  53. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_impact.py +0 -0
  54. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_incremental.py +0 -0
  55. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_indexing.py +0 -0
  56. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_integration.py +0 -0
  57. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_issue_cache.py +0 -0
  58. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_java_adapter.py +0 -0
  59. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_js_adapter.py +0 -0
  60. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_models.py +0 -0
  61. {codegraph_ai-0.2.2 → codegraph_ai-0.3.0}/tests/test_similar.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codegraph-ai
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: Hybrid graph + vector code intelligence powered by NeuG and zvec
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: neug
@@ -8,6 +8,9 @@ Requires-Dist: zvec
8
8
  Requires-Dist: tree-sitter-language-pack
9
9
  Requires-Dist: sentence-transformers
10
10
  Requires-Dist: numpy
11
+ Requires-Dist: jinja2
12
+ Requires-Dist: pyyaml
13
+ Requires-Dist: click
11
14
  Provides-Extra: server
12
15
  Requires-Dist: fastmcp; extra == "server"
13
16
  Provides-Extra: dev
@@ -164,6 +164,7 @@ def cmd_init(args: argparse.Namespace) -> None:
164
164
  print()
165
165
  print(f"Use 'codegraph status' to see detailed state.")
166
166
  print(f"Use 'codegraph query \"<question>\"' to ask questions.")
167
+ print(f"Use 'codegraph pr-review prepare' to review pull requests.")
167
168
  finally:
168
169
  cs.close()
169
170
 
@@ -522,6 +523,192 @@ def cmd_server(args: argparse.Namespace) -> None:
522
523
  mcp.run()
523
524
 
524
525
 
526
+ def cmd_pr_review(args: argparse.Namespace) -> None:
527
+ """Dispatch to pr-review prepare or pr-review label."""
528
+ action = getattr(args, 'pr_action', None)
529
+ if action == 'label':
530
+ cmd_pr_review_label(args)
531
+ elif action == 'prepare':
532
+ cmd_pr_review_prepare(args)
533
+ else:
534
+ # No subcommand given
535
+ print("Error: 'pr-review' requires a subcommand: 'prepare' for analysis or 'label' for labeling. Note that 'prepare' must be run before 'label'.")
536
+ print(" Usage: codegraph pr-review prepare --db .codegraph")
537
+ print(" codegraph pr-review label --db .codegraph")
538
+ sys.exit(1)
539
+
540
+
541
+ def cmd_pr_review_prepare(args: argparse.Namespace) -> None:
542
+ """Analyze PRs + detect conflicts + write to graph DB (full rebuild)."""
543
+ from codegraph.pr_review import run_prepare
544
+
545
+ repo_dir = _derive_repo_dir(args.db)
546
+
547
+ # Use explicit --repo or auto-detect from git remote
548
+ repo_name = args.repo
549
+ if not repo_name:
550
+ repo_name = _auto_detect_repo(None, repo_dir)
551
+ if not repo_name:
552
+ print("Error: Cannot auto-detect GitHub repo from git remote. Specify --repo owner/repo")
553
+ return
554
+
555
+ run_prepare(
556
+ db_dir=args.db,
557
+ repo_dir=repo_dir,
558
+ repo=repo_name,
559
+ author=getattr(args, 'author', ''),
560
+ output_dir=getattr(args, 'output', None),
561
+ skip_single_pr=getattr(args, 'skip_single_pr', False),
562
+ )
563
+
564
+
565
+ def cmd_pr_review_label(args: argparse.Namespace) -> None:
566
+ """Apply GitHub labels + post conflict comments from graph DB."""
567
+ from codegraph.pr_review import run_label
568
+
569
+ repo_dir = _derive_repo_dir(args.db)
570
+
571
+ # Use explicit --repo or auto-detect from git remote
572
+ repo_name = args.repo
573
+ if not repo_name:
574
+ repo_name = _auto_detect_repo(None, repo_dir)
575
+ if not repo_name:
576
+ print("Error: Cannot auto-detect GitHub repo from git remote. Specify --repo owner/repo")
577
+ return
578
+
579
+ run_label(
580
+ db_dir=args.db,
581
+ repo_dir=repo_dir,
582
+ repo=repo_name,
583
+ dry_run=getattr(args, 'dry_run', False),
584
+ )
585
+
586
+
587
+ def _auto_detect_repo(repo_arg, repo_dir: str) -> str | None:
588
+ """Auto-detect GitHub repo (owner/repo) from git remote if not specified."""
589
+ if repo_arg:
590
+ return repo_arg
591
+ import subprocess
592
+ try:
593
+ result = subprocess.run(
594
+ ['git', 'remote', 'get-url', 'origin'],
595
+ cwd=repo_dir, capture_output=True, text=True
596
+ )
597
+ if result.returncode == 0:
598
+ remote_url = result.stdout.strip()
599
+ import re
600
+ m = re.match(r'(?:https?://github\.com/|git@github\.com:)([^/]+/[^/]+?)(?:\.git)?$', remote_url)
601
+ if m:
602
+ detected = m.group(1)
603
+ print(f"[auto] --repo not specified, detected from remote: {detected}")
604
+ return detected
605
+ except Exception:
606
+ pass
607
+ return None
608
+
609
+
610
+ def _derive_repo_dir(db_path: str) -> str:
611
+ """Derive the local repository path from the --db directory.
612
+
613
+ When --db points to ``<repo>/.codegraph``, returns ``<repo>``;
614
+ otherwise falls back to the current working directory.
615
+ """
616
+ abs_db = os.path.abspath(db_path)
617
+ if os.path.basename(abs_db) == '.codegraph':
618
+ return os.path.dirname(abs_db)
619
+ return os.getcwd()
620
+
621
+
622
+ def cmd_explore(args: argparse.Namespace) -> None:
623
+ """Run guided exploration with interactive question answering."""
624
+ db_dir = _find_db(args.db)
625
+
626
+ if not os.path.isdir(db_dir):
627
+ print(f"Error: database not found at {db_dir}", file=sys.stderr)
628
+ print(f"Run 'codegraph init' first.", file=sys.stderr)
629
+ sys.exit(1)
630
+
631
+ cs = _open_cs(db_dir)
632
+ try:
633
+ from codegraph.guided_explorer import (
634
+ InteractiveExplorer,
635
+ PatternDetector,
636
+ QuestionGenerator,
637
+ )
638
+
639
+ # ── General exploration mode ───────────────────────────────────────
640
+ print("\nDetecting code patterns...")
641
+ detector = PatternDetector(neug_conn=cs.conn)
642
+ patterns = detector.detect_all_patterns(cs.conn)
643
+
644
+ if not patterns:
645
+ print("\nNo significant patterns detected. Your codebase appears healthy!")
646
+ return
647
+
648
+ print(f"Found {len(patterns)} patterns")
649
+
650
+ # Pre-filter patterns by --type before question generation,
651
+ # so that rare categories (e.g. pr-review) aren't truncated by top_n
652
+ if args.type != 'all':
653
+ from codegraph.models import PatternType
654
+ _TYPE_TO_PATTERN_TYPES = {
655
+ 'architecture': {PatternType.CYCLIC_DEPENDENCY, PatternType.HIGH_COUPLING,
656
+ PatternType.ISOLATED_MODULE, PatternType.STABLE_CORE},
657
+ 'risk': {PatternType.HIGH_FAN_IN, PatternType.HIGH_IMPACT_PR,
658
+ PatternType.PR_NO_TEST, PatternType.PR_INTERFACE_CHANGE,
659
+ PatternType.PR_DEAD_CODE},
660
+ 'evolution': {PatternType.EVOLUTION_HOTSPOT, PatternType.STABLE_CORE},
661
+ 'hotspot': {PatternType.HIGH_FAN_IN, PatternType.EVOLUTION_HOTSPOT},
662
+ 'pr-review': {PatternType.HIGH_IMPACT_PR, PatternType.PR_CONFLICT,
663
+ PatternType.PR_NO_TEST, PatternType.PR_INTERFACE_CHANGE,
664
+ PatternType.PR_CROSS_MODULE, PatternType.PR_DEAD_CODE},
665
+ }
666
+ target_types = _TYPE_TO_PATTERN_TYPES.get(args.type)
667
+ if target_types:
668
+ patterns = [p for p in patterns if p.pattern_type in target_types]
669
+
670
+ # Generate questions
671
+ print("\nGenerating guided questions...")
672
+ generator = QuestionGenerator()
673
+ questions = generator.generate_questions(
674
+ patterns,
675
+ role=args.role,
676
+ top_n=args.top if args.top else 10,
677
+ )
678
+
679
+ # Filter by --type (based on question category) — secondary filter
680
+ if args.type != 'all' and questions:
681
+ from codegraph.models import QuestionCategory
682
+ _TYPE_TO_CATEGORY = {
683
+ 'architecture': QuestionCategory.ARCHITECTURE,
684
+ 'risk': QuestionCategory.RISK,
685
+ 'evolution': QuestionCategory.EVOLUTION,
686
+ 'hotspot': QuestionCategory.HOTSPOT,
687
+ 'pr-review': QuestionCategory.PR_IMPACT,
688
+ }
689
+ target_cat = _TYPE_TO_CATEGORY[args.type]
690
+ questions = [q for q in questions if q.category == target_cat]
691
+
692
+ if not questions:
693
+ print(f"\nNo {args.type} questions generated from detected patterns.")
694
+ return
695
+
696
+ print(f"Generated {len(questions)} questions (type={args.type})")
697
+
698
+ # Run exploration
699
+ explorer = InteractiveExplorer(codescope_instance=cs, neug_conn=cs.conn)
700
+
701
+ if args.top:
702
+ # Non-interactive mode: just show top N questions
703
+ explorer.run_non_interactive(questions, top_n=args.top)
704
+ else:
705
+ # Interactive mode
706
+ explorer.run_interactive_session(questions)
707
+
708
+ finally:
709
+ cs.close()
710
+
711
+
525
712
  # =========================================================================
526
713
  # Main
527
714
  # =========================================================================
@@ -611,6 +798,35 @@ def main() -> None:
611
798
  p_server.add_argument("--db", default=None, help=f"Database directory (default: {DB_DEFAULT})")
612
799
  p_server.add_argument("--repo", default=None, help="Path to repository")
613
800
 
801
+ # -- pr-review --
802
+ p_pr = sub.add_parser("pr-review", help="PR review: prepare (analyze + write to DB) or label (apply labels + comments)")
803
+ p_pr_sub = p_pr.add_subparsers(dest="pr_action")
804
+
805
+ # pr-review prepare
806
+ p_pr_prepare = p_pr_sub.add_parser("prepare", help="Analyze PRs + detect conflicts + write to graph DB (full rebuild)")
807
+ p_pr_prepare.add_argument("--db", required=True, help="Path to the codegraph database directory")
808
+ p_pr_prepare.add_argument("--repo", default=None, help="GitHub repository in owner/repo format (default: auto-detect from git remote)")
809
+ p_pr_prepare.add_argument('--author', default="", help="Filter PRs by GitHub login")
810
+ p_pr_prepare.add_argument('--output', default=None, help="Output directory for HTML/MD files")
811
+ p_pr_prepare.add_argument('--skip-single-pr', action='store_true',
812
+ help="Skip per-PR risk scoring; only cross-PR conflict analysis")
813
+
814
+ # pr-review label
815
+ p_pr_label = p_pr_sub.add_parser("label", help="Apply GitHub labels + post conflict comments from graph DB")
816
+ p_pr_label.add_argument("--db", required=True, help="Path to the codegraph database directory")
817
+ p_pr_label.add_argument("--repo", default=None, help="GitHub repository in owner/repo format (default: auto-detect from git remote)")
818
+ p_pr_label.add_argument('--dry-run', action='store_true', help="Preview labels and comments without making API calls")
819
+
820
+ # -- explore --
821
+ p_explore = sub.add_parser("explore", help="Guided exploration with interactive questions (incl. PR follow-ups)")
822
+ p_explore.add_argument("--db", default=None, help=f"Database directory (default: {DB_DEFAULT})")
823
+ p_explore.add_argument("--role", default="architect",
824
+ help="User role: architect, maintainer, reviewer (default: architect)")
825
+ p_explore.add_argument("--top", type=int, default=None,
826
+ help="Show top N questions (non-interactive mode)")
827
+ p_explore.add_argument("--type", choices=['all', 'architecture', 'risk', 'evolution', 'hotspot', 'pr-review'], default='all',
828
+ help="Question type filter: all (default), architecture, risk, evolution, hotspot, pr-review")
829
+
614
830
  args = parser.parse_args()
615
831
 
616
832
  if not args.command:
@@ -629,6 +845,8 @@ def main() -> None:
629
845
  "analyze-bug": cmd_analyze_bug,
630
846
  "analyze-bugs": cmd_analyze_bugs,
631
847
  "server": cmd_server,
848
+ "pr-review": cmd_pr_review,
849
+ "explore": cmd_explore,
632
850
  }
633
851
  dispatch[args.command](args)
634
852