codebrain 0.1.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 (44) hide show
  1. codebrain/__init__.py +3 -0
  2. codebrain/__main__.py +6 -0
  3. codebrain/agent_bridge.py +162 -0
  4. codebrain/analyzer.py +943 -0
  5. codebrain/api.py +578 -0
  6. codebrain/api_models.py +102 -0
  7. codebrain/cli.py +1927 -0
  8. codebrain/comprehension.py +1939 -0
  9. codebrain/config.py +46 -0
  10. codebrain/context.py +276 -0
  11. codebrain/export.py +334 -0
  12. codebrain/graph/__init__.py +0 -0
  13. codebrain/graph/query.py +656 -0
  14. codebrain/graph/schema.py +113 -0
  15. codebrain/graph/store.py +295 -0
  16. codebrain/hook_runner.py +71 -0
  17. codebrain/hooks.py +107 -0
  18. codebrain/indexer.py +450 -0
  19. codebrain/llm.py +676 -0
  20. codebrain/logging.py +42 -0
  21. codebrain/mcp_server.py +1635 -0
  22. codebrain/memory/__init__.py +5 -0
  23. codebrain/memory/store.py +270 -0
  24. codebrain/parser/__init__.py +0 -0
  25. codebrain/parser/base.py +27 -0
  26. codebrain/parser/config_parser.py +228 -0
  27. codebrain/parser/models.py +44 -0
  28. codebrain/parser/python_parser.py +658 -0
  29. codebrain/parser/registry.py +144 -0
  30. codebrain/parser/typescript_parser.py +1189 -0
  31. codebrain/parser/typescript_treesitter.py +535 -0
  32. codebrain/py.typed +0 -0
  33. codebrain/resolver.py +171 -0
  34. codebrain/settings.py +88 -0
  35. codebrain/utils.py +59 -0
  36. codebrain/validator.py +563 -0
  37. codebrain/watcher/__init__.py +0 -0
  38. codebrain/watcher/file_watcher.py +173 -0
  39. codebrain-0.1.0.dist-info/METADATA +360 -0
  40. codebrain-0.1.0.dist-info/RECORD +44 -0
  41. codebrain-0.1.0.dist-info/WHEEL +5 -0
  42. codebrain-0.1.0.dist-info/entry_points.txt +6 -0
  43. codebrain-0.1.0.dist-info/licenses/LICENSE +21 -0
  44. codebrain-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,144 @@
1
+ """Parser registry — dispatches file parsing to the correct language parser."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from codebrain.logging import get_logger
8
+ from codebrain.parser.base import BaseParser
9
+ from codebrain.parser.models import ParsedFile
10
+
11
+ _log = get_logger("parser.registry")
12
+
13
+ # Module-level singleton
14
+ _registry: "ParserRegistry | None" = None
15
+
16
+
17
+ class ParserRegistry:
18
+ """Manages registered language parsers and dispatches parse requests."""
19
+
20
+ def __init__(self) -> None:
21
+ self._parsers: list[BaseParser] = []
22
+ self._ext_map: dict[str, BaseParser] = {}
23
+ self._name_map: dict[str, BaseParser] = {}
24
+
25
+ def register(self, parser: BaseParser) -> None:
26
+ """Register a parser instance."""
27
+ self._parsers.append(parser)
28
+ for ext in parser.extensions():
29
+ if ext in self._ext_map:
30
+ _log.debug(
31
+ "Extension %s already registered by %s, overriding with %s",
32
+ ext,
33
+ type(self._ext_map[ext]).__name__,
34
+ type(parser).__name__,
35
+ )
36
+ self._ext_map[ext] = parser
37
+
38
+ def register_name(self, name: str, parser: BaseParser) -> None:
39
+ """Register a parser for a specific filename (e.g. 'docker-compose.yml')."""
40
+ self._name_map[name] = parser
41
+
42
+ def can_parse(self, path: Path) -> bool:
43
+ """Return True if any registered parser handles this file."""
44
+ return path.suffix in self._ext_map or path.name in self._name_map
45
+
46
+ def parse(self, path: Path, repo_root: Path) -> ParsedFile:
47
+ """Parse a file using the appropriate registered parser.
48
+
49
+ Raises ValueError if no parser handles this file.
50
+ """
51
+ # Name-based match takes priority
52
+ parser = self._name_map.get(path.name)
53
+ if parser is None:
54
+ parser = self._ext_map.get(path.suffix)
55
+ if parser is None:
56
+ raise ValueError(f"No parser registered for '{path.name}' (ext '{path.suffix}')")
57
+ return parser.parse(path, repo_root)
58
+
59
+ @property
60
+ def supported_extensions(self) -> frozenset[str]:
61
+ """Return all extensions supported by registered parsers."""
62
+ return frozenset(self._ext_map.keys())
63
+
64
+ @property
65
+ def supported_names(self) -> frozenset[str]:
66
+ """Return all filenames with name-based parser matching."""
67
+ return frozenset(self._name_map.keys())
68
+
69
+ @property
70
+ def parsers(self) -> list[BaseParser]:
71
+ """Return list of registered parsers."""
72
+ return list(self._parsers)
73
+
74
+ def load_entrypoints(self) -> None:
75
+ """Discover and load parsers from the ``codebrain.parsers`` entry-points group."""
76
+ try:
77
+ from importlib.metadata import entry_points
78
+ except ImportError:
79
+ return
80
+
81
+ eps = entry_points()
82
+ # Python 3.12+ returns a SelectableGroups, older returns dict
83
+ if hasattr(eps, "select"):
84
+ group = eps.select(group="codebrain.parsers")
85
+ elif isinstance(eps, dict):
86
+ group = eps.get("codebrain.parsers", [])
87
+ else:
88
+ group = [ep for ep in eps if ep.group == "codebrain.parsers"]
89
+
90
+ for ep in group:
91
+ try:
92
+ parser_cls = ep.load()
93
+ # Skip entry-point parsers whose extensions are all already
94
+ # covered by built-in parsers (avoids regex overriding tree-sitter).
95
+ parser = parser_cls()
96
+ new_exts = parser.extensions() - frozenset(self._ext_map.keys())
97
+ if not new_exts:
98
+ _log.debug(
99
+ "Skipping plugin %s (%s) — all extensions already registered",
100
+ ep.name, type(parser).__name__,
101
+ )
102
+ continue
103
+ self.register(parser)
104
+ _log.debug("Loaded plugin parser: %s (%s)", ep.name, type(parser).__name__)
105
+ except Exception as exc:
106
+ _log.warning("Failed to load parser plugin %s: %s", ep.name, exc)
107
+
108
+
109
+ def get_registry() -> ParserRegistry:
110
+ """Return the global parser registry, initializing it on first call."""
111
+ global _registry
112
+ if _registry is None:
113
+ _registry = ParserRegistry()
114
+ _init_builtin_parsers(_registry)
115
+ _registry.load_entrypoints()
116
+ return _registry
117
+
118
+
119
+ def _init_builtin_parsers(registry: ParserRegistry) -> None:
120
+ """Register the built-in Python, TypeScript, and config parsers."""
121
+ from codebrain.parser.python_parser import PythonParser
122
+ from codebrain.parser.config_parser import ConfigParser
123
+
124
+ registry.register(PythonParser())
125
+
126
+ # Prefer tree-sitter AST parser for TypeScript/JavaScript when available
127
+ try:
128
+ from codebrain.parser.typescript_treesitter import TypeScriptTreeSitterParser, _TS_AVAILABLE
129
+ if _TS_AVAILABLE:
130
+ registry.register(TypeScriptTreeSitterParser())
131
+ _log.debug("Using tree-sitter AST parser for TypeScript/JavaScript")
132
+ else:
133
+ from codebrain.parser.typescript_parser import TypeScriptParser
134
+ registry.register(TypeScriptParser())
135
+ _log.debug("tree-sitter not available, using regex parser for TypeScript/JavaScript")
136
+ except ImportError:
137
+ from codebrain.parser.typescript_parser import TypeScriptParser
138
+ registry.register(TypeScriptParser())
139
+
140
+ # Config parser: .env by extension, docker-compose by name
141
+ config = ConfigParser()
142
+ registry.register(config)
143
+ for name in config._COMPOSE_NAMES:
144
+ registry.register_name(name, config)