thailint 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.
@@ -0,0 +1,87 @@
1
+ """
2
+ Purpose: Nesting depth linter package initialization
3
+
4
+ Scope: Exports for nesting depth linter module
5
+
6
+ Overview: Initializes the nesting depth linter package and exposes the main rule class for
7
+ external use. Exports NestingDepthRule as the primary interface for the nesting linter,
8
+ allowing the orchestrator to discover and instantiate the rule. Also exports configuration
9
+ and analyzer classes for advanced use cases. Provides a convenience lint() function for
10
+ direct usage without orchestrator setup. This module serves as the entry point for
11
+ the nesting linter functionality within the thai-lint framework.
12
+
13
+ Dependencies: NestingDepthRule, NestingConfig, PythonNestingAnalyzer, TypeScriptNestingAnalyzer
14
+
15
+ Exports: NestingDepthRule (primary), NestingConfig, PythonNestingAnalyzer, TypeScriptNestingAnalyzer, lint
16
+
17
+ Interfaces: Standard Python package initialization with __all__ for explicit exports, lint() convenience function
18
+
19
+ Implementation: Simple re-export pattern for package interface, convenience function wraps orchestrator
20
+ """
21
+
22
+ from pathlib import Path
23
+ from typing import Any
24
+
25
+ from .config import NestingConfig
26
+ from .linter import NestingDepthRule
27
+ from .python_analyzer import PythonNestingAnalyzer
28
+ from .typescript_analyzer import TypeScriptNestingAnalyzer
29
+
30
+ __all__ = [
31
+ "NestingDepthRule",
32
+ "NestingConfig",
33
+ "PythonNestingAnalyzer",
34
+ "TypeScriptNestingAnalyzer",
35
+ "lint",
36
+ ]
37
+
38
+
39
+ def lint(path: Path | str, config: dict[str, Any] | None = None, max_depth: int = 4) -> list:
40
+ """Lint a file or directory for nesting depth violations.
41
+
42
+ Args:
43
+ path: Path to file or directory to lint
44
+ config: Configuration dict (optional, uses defaults if not provided)
45
+ max_depth: Maximum allowed nesting depth (default: 4)
46
+
47
+ Returns:
48
+ List of violations found
49
+
50
+ Example:
51
+ >>> from src.linters.nesting import lint
52
+ >>> violations = lint('src/my_module.py', max_depth=3)
53
+ >>> for v in violations:
54
+ ... print(f"{v.file_path}:{v.line} - {v.message}")
55
+ """
56
+ path_obj = Path(path) if isinstance(path, str) else path
57
+ project_root = path_obj if path_obj.is_dir() else path_obj.parent
58
+
59
+ orchestrator = _setup_nesting_orchestrator(project_root, config, max_depth)
60
+ violations = _execute_nesting_lint(orchestrator, path_obj)
61
+
62
+ return [v for v in violations if "nesting" in v.rule_id]
63
+
64
+
65
+ def _setup_nesting_orchestrator(
66
+ project_root: Path, config: dict[str, Any] | None, max_depth: int
67
+ ) -> Any:
68
+ """Set up orchestrator with nesting config."""
69
+ from src.orchestrator.core import Orchestrator
70
+
71
+ orchestrator = Orchestrator(project_root=project_root)
72
+
73
+ if config:
74
+ orchestrator.config["nesting"] = config
75
+ else:
76
+ orchestrator.config["nesting"] = {"max_nesting_depth": max_depth}
77
+
78
+ return orchestrator
79
+
80
+
81
+ def _execute_nesting_lint(orchestrator: Any, path_obj: Path) -> list:
82
+ """Execute linting on file or directory."""
83
+ if path_obj.is_file():
84
+ return orchestrator.lint_file(path_obj)
85
+ if path_obj.is_dir():
86
+ return orchestrator.lint_directory(path_obj)
87
+ return []
@@ -0,0 +1,50 @@
1
+ """
2
+ Purpose: Configuration schema for nesting depth linter
3
+
4
+ Scope: NestingConfig dataclass with max_nesting_depth setting
5
+
6
+ Overview: Defines configuration schema for nesting depth linter. Provides NestingConfig dataclass
7
+ with max_nesting_depth field (default 4), validation logic, and config loading from YAML/JSON.
8
+ Supports per-file and per-directory config overrides. Validates that max_depth is positive
9
+ integer. Integrates with the orchestrator's configuration system to allow users to customize
10
+ nesting depth limits via .thailint.yaml configuration files.
11
+
12
+ Dependencies: dataclasses, typing
13
+
14
+ Exports: NestingConfig dataclass
15
+
16
+ Interfaces: NestingConfig(max_nesting_depth: int = 4), from_dict class method for loading config
17
+
18
+ Implementation: Dataclass with validation and defaults, matches reference implementation default
19
+ """
20
+
21
+ from dataclasses import dataclass
22
+ from typing import Any
23
+
24
+
25
+ @dataclass
26
+ class NestingConfig:
27
+ """Configuration for nesting depth linter."""
28
+
29
+ max_nesting_depth: int = 4 # Default from reference implementation
30
+ enabled: bool = True
31
+
32
+ def __post_init__(self) -> None:
33
+ """Validate configuration values."""
34
+ if self.max_nesting_depth <= 0:
35
+ raise ValueError(f"max_nesting_depth must be positive, got {self.max_nesting_depth}")
36
+
37
+ @classmethod
38
+ def from_dict(cls, config: dict[str, Any]) -> "NestingConfig":
39
+ """Load configuration from dictionary.
40
+
41
+ Args:
42
+ config: Dictionary containing configuration values
43
+
44
+ Returns:
45
+ NestingConfig instance with values from dictionary
46
+ """
47
+ return cls(
48
+ max_nesting_depth=config.get("max_nesting_depth", 4),
49
+ enabled=config.get("enabled", True),
50
+ )
@@ -0,0 +1,257 @@
1
+ """
2
+ Purpose: Main nesting depth linter rule implementation
3
+
4
+ Scope: NestingDepthRule class implementing BaseLintRule interface
5
+
6
+ Overview: Implements nesting depth linter rule following BaseLintRule interface. Detects excessive
7
+ nesting depth in Python and TypeScript code using AST analysis. Supports configurable
8
+ max_nesting_depth limit (default 4). Provides helpful violation messages with refactoring
9
+ suggestions (early returns, guard clauses, extract method). Integrates with orchestrator for
10
+ automatic rule discovery. Handles both Python (using ast) and TypeScript (using typescript-estree)
11
+ code analysis. Gracefully handles syntax errors by reporting them as violations rather than
12
+ crashing. Supports configuration loading from context metadata for per-file customization.
13
+
14
+ Dependencies: BaseLintRule, BaseLintContext, Violation, PythonNestingAnalyzer, TypeScriptNestingAnalyzer
15
+
16
+ Exports: NestingDepthRule class
17
+
18
+ Interfaces: NestingDepthRule.check(context) -> list[Violation], properties for rule metadata
19
+
20
+ Implementation: AST-based analysis with configurable limits and helpful error messages
21
+ """
22
+
23
+ import ast
24
+ from typing import Any
25
+
26
+ from src.core.base import BaseLintContext, BaseLintRule
27
+ from src.core.types import Severity, Violation
28
+ from src.linter_config.ignore import IgnoreDirectiveParser
29
+
30
+ from .config import NestingConfig
31
+ from .python_analyzer import PythonNestingAnalyzer
32
+ from .typescript_analyzer import TypeScriptNestingAnalyzer
33
+
34
+
35
+ class NestingDepthRule(BaseLintRule):
36
+ """Detects excessive nesting depth in functions."""
37
+
38
+ def __init__(self) -> None:
39
+ """Initialize the nesting depth rule."""
40
+ self._ignore_parser = IgnoreDirectiveParser()
41
+
42
+ @property
43
+ def rule_id(self) -> str:
44
+ """Unique identifier for this rule."""
45
+ return "nesting.excessive-depth"
46
+
47
+ @property
48
+ def rule_name(self) -> str:
49
+ """Human-readable name for this rule."""
50
+ return "Excessive Nesting Depth"
51
+
52
+ @property
53
+ def description(self) -> str:
54
+ """Description of what this rule checks."""
55
+ return "Functions should not have excessive nesting depth for better readability"
56
+
57
+ def check(self, context: BaseLintContext) -> list[Violation]:
58
+ """Check for excessive nesting depth violations.
59
+
60
+ Args:
61
+ context: Lint context with file information
62
+
63
+ Returns:
64
+ List of violations found
65
+ """
66
+ if context.file_content is None:
67
+ return []
68
+
69
+ # Load configuration
70
+ config = self._load_config(context)
71
+ if not config.enabled:
72
+ return []
73
+
74
+ # Analyze based on language
75
+ if context.language == "python":
76
+ return self._check_python(context, config)
77
+ if context.language in ("typescript", "javascript"):
78
+ return self._check_typescript(context, config)
79
+ return []
80
+
81
+ def _load_config(self, context: BaseLintContext) -> NestingConfig:
82
+ """Load configuration from context metadata.
83
+
84
+ Args:
85
+ context: Lint context containing metadata
86
+
87
+ Returns:
88
+ NestingConfig instance with configuration values
89
+ """
90
+ metadata = getattr(context, "metadata", None)
91
+ if metadata is None or not isinstance(metadata, dict):
92
+ return NestingConfig()
93
+
94
+ config_dict = metadata.get("nesting", {})
95
+ if not isinstance(config_dict, dict):
96
+ return NestingConfig()
97
+
98
+ return NestingConfig.from_dict(config_dict)
99
+
100
+ def _check_python(self, context: BaseLintContext, config: NestingConfig) -> list[Violation]:
101
+ """Check Python code for nesting violations.
102
+
103
+ Args:
104
+ context: Lint context with Python file information
105
+ config: Nesting configuration
106
+
107
+ Returns:
108
+ List of violations found in Python code
109
+ """
110
+ try:
111
+ tree = ast.parse(context.file_content or "")
112
+ except SyntaxError as e:
113
+ return [self._create_syntax_error_violation(e, context)]
114
+
115
+ analyzer = PythonNestingAnalyzer()
116
+ functions = analyzer.find_all_functions(tree)
117
+ return self._check_functions(functions, config, context)
118
+
119
+ def _check_functions(
120
+ self,
121
+ functions: list[ast.FunctionDef | ast.AsyncFunctionDef],
122
+ config: NestingConfig,
123
+ context: BaseLintContext,
124
+ ) -> list[Violation]:
125
+ """Check list of functions for nesting violations."""
126
+ violations = []
127
+ for func in functions:
128
+ violation = self._check_single_function(func, config, context)
129
+ if violation:
130
+ violations.append(violation)
131
+ return violations
132
+
133
+ def _check_single_function(
134
+ self,
135
+ func: ast.FunctionDef | ast.AsyncFunctionDef,
136
+ config: NestingConfig,
137
+ context: BaseLintContext,
138
+ ) -> Violation | None:
139
+ """Check a single function for nesting violations."""
140
+ analyzer = PythonNestingAnalyzer()
141
+ max_depth, _line = analyzer.calculate_max_depth(func)
142
+
143
+ if max_depth <= config.max_nesting_depth:
144
+ return None
145
+
146
+ violation = self._create_nesting_violation(func, max_depth, config, context)
147
+ if self._ignore_parser.should_ignore_violation(violation, context.file_content or ""):
148
+ return None
149
+
150
+ return violation
151
+
152
+ def _create_syntax_error_violation(
153
+ self, error: SyntaxError, context: BaseLintContext
154
+ ) -> Violation:
155
+ """Create violation for syntax error."""
156
+ return Violation(
157
+ rule_id=self.rule_id,
158
+ file_path=str(context.file_path or ""),
159
+ line=error.lineno or 0,
160
+ column=error.offset or 0,
161
+ message=f"Syntax error: {error.msg}",
162
+ severity=Severity.ERROR,
163
+ suggestion="Fix syntax errors before checking nesting depth",
164
+ )
165
+
166
+ def _create_nesting_violation(
167
+ self,
168
+ func: ast.FunctionDef | ast.AsyncFunctionDef,
169
+ max_depth: int,
170
+ config: NestingConfig,
171
+ context: BaseLintContext,
172
+ ) -> Violation:
173
+ """Create violation for excessive nesting."""
174
+ return Violation(
175
+ rule_id=self.rule_id,
176
+ file_path=str(context.file_path or ""),
177
+ line=func.lineno,
178
+ column=func.col_offset,
179
+ message=f"Function '{func.name}' has excessive nesting depth ({max_depth})",
180
+ severity=Severity.ERROR,
181
+ suggestion=(
182
+ f"Maximum nesting depth of {max_depth} exceeds limit of {config.max_nesting_depth}. "
183
+ "Consider extracting nested logic to separate functions, using early returns, "
184
+ "or applying guard clauses to reduce nesting."
185
+ ),
186
+ )
187
+
188
+ def _check_typescript(self, context: BaseLintContext, config: NestingConfig) -> list[Violation]:
189
+ """Check TypeScript code for nesting violations."""
190
+ analyzer = TypeScriptNestingAnalyzer()
191
+ root_node = analyzer.parse_typescript(context.file_content or "")
192
+
193
+ if root_node is None:
194
+ return []
195
+
196
+ functions = analyzer.find_all_functions(root_node)
197
+ return self._check_typescript_functions(functions, config, context)
198
+
199
+ def _check_typescript_functions(
200
+ self, functions: list, config: NestingConfig, context: BaseLintContext
201
+ ) -> list[Violation]:
202
+ """Check TypeScript functions for nesting violations."""
203
+ violations = []
204
+
205
+ for func_node, func_name in functions:
206
+ violation = self._check_single_typescript_function(
207
+ func_node, func_name, config, context
208
+ )
209
+ if violation:
210
+ violations.append(violation)
211
+
212
+ return violations
213
+
214
+ def _check_single_typescript_function(
215
+ self, func_node: Any, func_name: str, config: NestingConfig, context: BaseLintContext
216
+ ) -> Violation | None:
217
+ """Check a single TypeScript function for nesting violations."""
218
+ analyzer = TypeScriptNestingAnalyzer()
219
+ max_depth, _line = analyzer.calculate_max_depth(func_node)
220
+
221
+ if max_depth <= config.max_nesting_depth:
222
+ return None
223
+
224
+ violation = self._create_typescript_nesting_violation(
225
+ func_node, func_name, max_depth, config, context
226
+ )
227
+
228
+ if self._ignore_parser.should_ignore_violation(violation, context.file_content or ""):
229
+ return None
230
+
231
+ return violation
232
+
233
+ def _create_typescript_nesting_violation( # pylint: disable=too-many-arguments,too-many-positional-arguments
234
+ self,
235
+ func_node: Any, # tree-sitter Node
236
+ func_name: str,
237
+ max_depth: int,
238
+ config: NestingConfig,
239
+ context: BaseLintContext,
240
+ ) -> Violation:
241
+ """Create violation for excessive nesting in TypeScript."""
242
+ line = func_node.start_point[0] + 1 # Convert to 1-indexed
243
+ column = func_node.start_point[1]
244
+
245
+ return Violation(
246
+ rule_id=self.rule_id,
247
+ file_path=str(context.file_path or ""),
248
+ line=line,
249
+ column=column,
250
+ message=f"Function '{func_name}' has excessive nesting depth ({max_depth})",
251
+ severity=Severity.ERROR,
252
+ suggestion=(
253
+ f"Maximum nesting depth of {max_depth} exceeds limit of {config.max_nesting_depth}. "
254
+ "Consider extracting nested logic to separate functions, using early returns, "
255
+ "or applying guard clauses to reduce nesting."
256
+ ),
257
+ )
@@ -0,0 +1,89 @@
1
+ """
2
+ Purpose: Python AST-based nesting depth calculator
3
+
4
+ Scope: Python code nesting depth analysis using ast module
5
+
6
+ Overview: Analyzes Python code to calculate maximum nesting depth using AST traversal. Implements
7
+ visitor pattern to walk AST, tracking current depth and maximum depth found. Increments depth
8
+ for If, For, While, With, AsyncWith, Try, ExceptHandler, Match, and match_case nodes. Starts
9
+ depth counting at 1 for function body, matching reference implementation behavior. Returns
10
+ maximum depth found and location information for violation reporting. Provides helper method
11
+ to find all function definitions in an AST tree for batch processing.
12
+
13
+ Dependencies: ast module for Python parsing
14
+
15
+ Exports: PythonNestingAnalyzer class with calculate_max_depth method
16
+
17
+ Interfaces: calculate_max_depth(func_node: ast.FunctionDef) -> tuple[int, int], find_all_functions
18
+
19
+ Implementation: AST visitor pattern with depth tracking, based on reference implementation algorithm
20
+ """
21
+
22
+ import ast
23
+
24
+
25
+ class PythonNestingAnalyzer:
26
+ """Calculates maximum nesting depth in Python functions."""
27
+
28
+ def calculate_max_depth(
29
+ self, func_node: ast.FunctionDef | ast.AsyncFunctionDef
30
+ ) -> tuple[int, int]:
31
+ """Calculate maximum nesting depth in a function.
32
+
33
+ Args:
34
+ func_node: AST node for function definition
35
+
36
+ Returns:
37
+ Tuple of (max_depth, line_number_of_max_depth)
38
+ """
39
+ max_depth = 0
40
+ max_depth_line = func_node.lineno
41
+
42
+ def visit_node(node: ast.AST, current_depth: int = 0) -> None:
43
+ nonlocal max_depth, max_depth_line
44
+
45
+ if current_depth > max_depth:
46
+ max_depth = current_depth
47
+ max_depth_line = getattr(node, "lineno", func_node.lineno)
48
+
49
+ # Nodes that increase nesting depth
50
+ if isinstance(
51
+ node,
52
+ (
53
+ ast.If,
54
+ ast.For,
55
+ ast.While,
56
+ ast.With,
57
+ ast.AsyncWith,
58
+ ast.Try,
59
+ ast.ExceptHandler,
60
+ ast.Match,
61
+ ast.match_case,
62
+ ),
63
+ ):
64
+ current_depth += 1
65
+
66
+ # Visit children
67
+ for child in ast.iter_child_nodes(node):
68
+ visit_node(child, current_depth)
69
+
70
+ # Start at depth 1 for function body (matching reference implementation)
71
+ for stmt in func_node.body:
72
+ visit_node(stmt, 1)
73
+
74
+ return max_depth, max_depth_line
75
+
76
+ def find_all_functions(self, tree: ast.AST) -> list[ast.FunctionDef | ast.AsyncFunctionDef]:
77
+ """Find all function definitions in AST.
78
+
79
+ Args:
80
+ tree: Python AST to search
81
+
82
+ Returns:
83
+ List of all FunctionDef and AsyncFunctionDef nodes found
84
+ """
85
+ functions = []
86
+ for node in ast.walk(tree):
87
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
88
+ functions.append(node)
89
+ return functions
@@ -0,0 +1,180 @@
1
+ """
2
+ Purpose: TypeScript AST-based nesting depth calculator
3
+
4
+ Scope: TypeScript code nesting depth analysis using tree-sitter parser
5
+
6
+ Overview: Analyzes TypeScript code to calculate maximum nesting depth using AST traversal.
7
+ Implements visitor pattern to walk TypeScript AST from tree-sitter, tracking current depth
8
+ and maximum depth found. Increments depth for if_statement, for_statement, for_in_statement,
9
+ while_statement, do_statement, try_statement, switch_statement nodes. Starts depth counting
10
+ at 1 for function body. Returns maximum depth found and location.
11
+
12
+ Dependencies: tree-sitter, tree-sitter-typescript for TypeScript parsing
13
+
14
+ Exports: TypeScriptNestingAnalyzer class with calculate_max_depth and parse_typescript methods
15
+
16
+ Interfaces: calculate_max_depth(func_node) -> tuple[int, int], parse_typescript(code: str)
17
+
18
+ Implementation: tree-sitter AST visitor pattern with depth tracking for TypeScript
19
+ """
20
+
21
+ from typing import Any
22
+
23
+ try:
24
+ import tree_sitter_typescript as tstypescript
25
+ from tree_sitter import Language, Node, Parser
26
+
27
+ TS_LANGUAGE = Language(tstypescript.language_typescript())
28
+ TS_PARSER = Parser(TS_LANGUAGE)
29
+ TREE_SITTER_AVAILABLE = True
30
+ except ImportError:
31
+ TREE_SITTER_AVAILABLE = False
32
+ TS_PARSER = None # type: ignore
33
+ Node = Any # type: ignore
34
+
35
+
36
+ class TypeScriptNestingAnalyzer:
37
+ """Calculates maximum nesting depth in TypeScript functions."""
38
+
39
+ # Tree-sitter node types that increase nesting depth
40
+ NESTING_NODE_TYPES = {
41
+ "if_statement",
42
+ "for_statement",
43
+ "for_in_statement",
44
+ "while_statement",
45
+ "do_statement",
46
+ "try_statement",
47
+ "switch_statement",
48
+ "with_statement", # Deprecated but exists
49
+ }
50
+
51
+ def parse_typescript(self, code: str) -> Node | None:
52
+ """Parse TypeScript code to AST using tree-sitter.
53
+
54
+ Args:
55
+ code: TypeScript source code to parse
56
+
57
+ Returns:
58
+ Tree-sitter AST root node, or None if parsing fails
59
+ """
60
+ if not TREE_SITTER_AVAILABLE or TS_PARSER is None:
61
+ return None
62
+
63
+ tree = TS_PARSER.parse(bytes(code, "utf8"))
64
+ return tree.root_node
65
+
66
+ def calculate_max_depth(self, func_node: Node) -> tuple[int, int]:
67
+ """Calculate maximum nesting depth in a TypeScript function."""
68
+ if not TREE_SITTER_AVAILABLE:
69
+ return 0, 0
70
+
71
+ body_node = self._find_function_body(func_node)
72
+ if not body_node:
73
+ return 0, func_node.start_point[0] + 1
74
+
75
+ return self._calculate_depth_in_body(body_node)
76
+
77
+ def _find_function_body(self, func_node: Node) -> Node | None:
78
+ """Find the statement_block node in a function."""
79
+ for child in func_node.children:
80
+ if child.type == "statement_block":
81
+ return child
82
+ return None
83
+
84
+ def _calculate_depth_in_body(self, body_node: Node) -> tuple[int, int]:
85
+ """Calculate max depth within a function body."""
86
+ max_depth = 0
87
+ max_depth_line = body_node.start_point[0] + 1
88
+
89
+ def visit_node(node: Node, current_depth: int = 0) -> None:
90
+ nonlocal max_depth, max_depth_line
91
+
92
+ if current_depth > max_depth:
93
+ max_depth = current_depth
94
+ max_depth_line = node.start_point[0] + 1
95
+
96
+ new_depth = current_depth + 1 if node.type in self.NESTING_NODE_TYPES else current_depth
97
+
98
+ for child in node.children:
99
+ visit_node(child, new_depth)
100
+
101
+ for child in body_node.children:
102
+ visit_node(child, 1)
103
+
104
+ return max_depth, max_depth_line
105
+
106
+ def find_all_functions(self, root_node: Node) -> list[tuple[Node, str]]:
107
+ """Find all function definitions in TypeScript AST.
108
+
109
+ Args:
110
+ root_node: Tree-sitter root node
111
+
112
+ Returns:
113
+ List of tuples: (function_node, function_name)
114
+ """
115
+ if not TREE_SITTER_AVAILABLE:
116
+ return []
117
+
118
+ functions: list[tuple[Node, str]] = []
119
+ self._collect_functions(root_node, functions)
120
+ return functions
121
+
122
+ def _collect_functions(self, node: Node, functions: list[tuple[Node, str]]) -> None:
123
+ """Recursively collect function nodes from AST."""
124
+ function_entry = self._extract_function_if_applicable(node)
125
+ if function_entry:
126
+ functions.append(function_entry)
127
+
128
+ for child in node.children:
129
+ self._collect_functions(child, functions)
130
+
131
+ def _extract_function_if_applicable(self, node: Node) -> tuple[Node, str] | None:
132
+ """Extract function node and name if node is a function type."""
133
+ if node.type == "function_declaration":
134
+ return self._extract_function_declaration(node)
135
+ if node.type == "arrow_function":
136
+ return self._extract_arrow_function(node)
137
+ if node.type == "method_definition":
138
+ return self._extract_method_definition(node)
139
+ if node.type in ("function_expression", "function"):
140
+ return self._extract_function_expression(node)
141
+ return None
142
+
143
+ def _extract_function_declaration(self, node: Node) -> tuple[Node, str]:
144
+ """Extract name from function declaration node."""
145
+ name_node = self._find_child_by_type(node, "identifier")
146
+ name = name_node.text.decode("utf8") if name_node and name_node.text else "<anonymous>"
147
+ return (node, name)
148
+
149
+ def _extract_arrow_function(self, node: Node) -> tuple[Node, str]:
150
+ """Extract name from arrow function node."""
151
+ name = "<arrow>"
152
+ parent = node.parent
153
+ if parent and parent.type == "variable_declarator":
154
+ id_node = self._find_child_by_type(parent, "identifier")
155
+ if id_node and id_node.text:
156
+ name = id_node.text.decode("utf8")
157
+ return (node, name)
158
+
159
+ def _extract_method_definition(self, node: Node) -> tuple[Node, str]:
160
+ """Extract name from method definition node."""
161
+ name_node = self._find_child_by_type(node, "property_identifier")
162
+ name = name_node.text.decode("utf8") if name_node and name_node.text else "<method>"
163
+ return (node, name)
164
+
165
+ def _extract_function_expression(self, node: Node) -> tuple[Node, str]:
166
+ """Extract name from function expression node."""
167
+ name = "<function>"
168
+ parent = node.parent
169
+ if parent and parent.type == "variable_declarator":
170
+ id_node = self._find_child_by_type(parent, "identifier")
171
+ if id_node and id_node.text:
172
+ name = id_node.text.decode("utf8")
173
+ return (node, name)
174
+
175
+ def _find_child_by_type(self, node: Node, child_type: str) -> Node | None:
176
+ """Find first child node matching the given type."""
177
+ for child in node.children:
178
+ if child.type == child_type:
179
+ return child
180
+ return None
@@ -0,0 +1,9 @@
1
+ """Multi-language orchestrator for coordinating linting operations.
2
+
3
+ This package provides the main orchestration engine that coordinates rule execution
4
+ across files and languages.
5
+ """
6
+
7
+ from .core import Orchestrator
8
+
9
+ __all__ = ["Orchestrator"]