Skip to content

codenib.code_chunking

Code chunking module for splitting source code files into semantic chunks.

Modules:

Name Description
base

Base code chunker class with common functionality.

cpp_chunker

C++ specific code chunker implementation.

csharp_chunker

C#-specific code chunker implementation.

go_chunker

Go-specific code chunker implementation.

java_chunker

Java-specific code chunker implementation.

js_chunker

JavaScript/TypeScript code chunker implementation.

kotlin_chunker

Kotlin-specific code chunker implementation.

lua_chunker

Lua-specific code chunker implementation.

php_chunker

PHP-specific code chunker implementation.

python_chunker

Python-specific code chunker implementation.

ruby_chunker

Ruby-specific code chunker implementation.

rust_chunker

Rust-specific code chunker implementation.

scala_chunker

Scala-specific code chunker implementation.

swift_chunker

Swift-specific code chunker implementation.

Classes:

Name Description
BaseCodeChunker

Base class for language-specific code chunkers.

CppCodeChunker

Code chunker specifically for C++ files.

CSharpCodeChunker

Code chunker for C# source files.

GoCodeChunker

Code chunker for Go source files.

JavaCodeChunker

Code chunker for Java source files.

JsTsCodeChunker

Chunker for JavaScript and TypeScript source files.

KotlinCodeChunker

Code chunker for Kotlin source files.

LuaCodeChunker

Code chunker for Lua source files.

PhpCodeChunker

Code chunker for PHP source files.

PythonCodeChunker

Code chunker specifically for Python files.

RubyCodeChunker

Code chunker for Ruby source files.

RustCodeChunker

Code chunker for Rust source files.

ScalaCodeChunker

Code chunker for Scala source files.

SwiftCodeChunker

Code chunker for Swift source files.

Functions:

Name Description
create_chunker

Create a code chunker for the specified language.

BaseCodeChunker

BaseCodeChunker(
    language: str,
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    include_header_epilogue: bool = False,
    l2_level_exclusive: bool = True,
    skeleton_mode: bool = False,
    include_l2_in_file_skeleton: bool = True,
)

Bases: ABC

Base class for language-specific code chunkers.

Parameters:

Name Type Description Default
language str

Programming language to parse ('python', 'cpp', 'java', etc.)

required
max_lines_per_chunk int | None

Maximum number of lines per emitted chunk. When set, large logical chunks (function/class) will be split into multiple sequential chunks of at most this many lines. node_id and name remain the same across the split pieces. Default: None (no splitting). Set to a number to enable.

None
chunk_depth int

Depth of AST traversal for chunking: 0 = Treat entire file as a single chunk 1 = Top-level only (classes and top-level functions, no methods) 2 = Method-level (classes, functions, and methods) [default]

2
include_header_epilogue bool

Whether to include file header (imports, module docstrings) and epilogue (trailing code) in chunks. Default: False (skip them to reduce noise).

False
l2_level_exclusive bool

When chunk_depth is 2 (method/member level), whether to exclude the containing type/scope chunks (classes, structs, impls). Default: True to keep L2-only output; set to False to emit both L1 container chunks and L2 members.

True
skeleton_mode bool

When True, emit skeletonized content (signatures only) for file- and type-level chunks. File-level skeletons list top-level definitions and, when include_l2_in_file_skeleton is True, member signatures for a hierarchical view. Type-level skeletons list member signatures. Defaults to False (emit full source).

False
include_l2_in_file_skeleton bool

When chunk_depth is 0 and skeleton_mode is enabled, whether to include L2/member signatures in the file skeleton output. Defaults to True for richer, hierarchical skeletons.

True

Methods:

Name Description
chunk_file

Chunk a code file into function/class level pieces.

save_chunks_to_json

Save chunks to a JSON file.

print_chunk_summary

Print a summary of the generated chunks.

Source code in codenib/code_chunking/base.py
def __init__(
    self,
    language: str,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    include_header_epilogue: bool = False,
    l2_level_exclusive: bool = True,
    skeleton_mode: bool = False,
    include_l2_in_file_skeleton: bool = True,
):
    """
    Initialize the code chunker for a specific language.

    Args:
        language: Programming language to parse ('python', 'cpp', 'java', etc.)
        max_lines_per_chunk: Maximum number of lines per emitted chunk. When set,
            large logical chunks (function/class) will be split into
            multiple sequential chunks of at most this many lines. node_id and name
            remain the same across the split pieces. Default: None (no splitting).
            Set to a number to enable.
        chunk_depth: Depth of AST traversal for chunking:
            0 = Treat entire file as a single chunk
            1 = Top-level only (classes and top-level functions, no methods)
            2 = Method-level (classes, functions, and methods) [default]
        include_header_epilogue: Whether to include file header (imports, module
            docstrings) and epilogue (trailing code) in chunks. Default: False
            (skip them to reduce noise).
        l2_level_exclusive: When chunk_depth is 2 (method/member level), whether
            to exclude the containing type/scope chunks (classes, structs, impls).
            Default: True to keep L2-only output; set to False to emit both L1
            container chunks and L2 members.
        skeleton_mode: When True, emit skeletonized content (signatures only) for
            file- and type-level chunks. File-level skeletons list top-level
            definitions and, when include_l2_in_file_skeleton is True, member
            signatures for a hierarchical view. Type-level skeletons list member
            signatures. Defaults to False (emit full source).
        include_l2_in_file_skeleton: When chunk_depth is 0 and skeleton_mode is
            enabled, whether to include L2/member signatures in the file skeleton
            output. Defaults to True for richer, hierarchical skeletons.
    """
    self.language = language
    self.max_lines_per_chunk = max_lines_per_chunk
    self.chunk_depth = chunk_depth
    self.include_header_epilogue = include_header_epilogue
    self.l2_level_exclusive = l2_level_exclusive
    self.skeleton_mode = skeleton_mode
    self.include_l2_in_file_skeleton = include_l2_in_file_skeleton
    try:
        self.tree_sitter_language = self._get_tree_sitter_language(language)
        self.parser = self._create_parser(self.tree_sitter_language)
        logger.info(
            "Successfully loaded %s language parser from tree-sitter-language-pack",
            language,
        )
    except Exception as e:
        logger.error("Error loading %s parser: %s", language, e)
        sys.exit(1)

chunk_file

chunk_file(
    file_path: str, relative_path: str | None = None, skeleton_mode: bool | None = None
) -> list[CodeChunk]

Chunk a code file into function/class level pieces.

Parameters:

Name Type Description Default
file_path str

Absolute path to the code file to chunk

required
relative_path str | None

Relative path for node_id generation

None
skeleton_mode bool | None

Override instance-level skeleton setting. When True, chunks contain signature-only skeletons instead of full bodies.

None

Returns:

Type Description
list[CodeChunk]

List of CodeChunk objects representing the chunks

Source code in codenib/code_chunking/base.py
def chunk_file(
    self,
    file_path: str,
    relative_path: Optional[str] = None,
    skeleton_mode: Optional[bool] = None,
) -> List[CodeChunk]:
    """
    Chunk a code file into function/class level pieces.

    Args:
        file_path: Absolute path to the code file to chunk
        relative_path: Relative path for node_id generation
        skeleton_mode: Override instance-level skeleton setting. When True, chunks contain
            signature-only skeletons instead of full bodies.

    Returns:
        List of CodeChunk objects representing the chunks
    """
    if not os.path.exists(file_path):
        logger.error(f"Error: File {file_path} not found")
        return []

    # logger.debug(f"Chunking file: {file_path}")

    # Read the file
    with open(file_path, "r", encoding="utf-8") as f:
        code_content = f.read()

    # Parse the code
    code_bytes = code_content.encode("utf-8")
    tree = self.parser.parse(code_bytes)
    root_node = tree.root_node

    use_skeleton = self.skeleton_mode if skeleton_mode is None else skeleton_mode
    include_l2_in_skeleton = (
        use_skeleton and self.chunk_depth == 0 and self.include_l2_in_file_skeleton
    )

    # Split content into lines for easier chunk extraction
    lines = code_content.split("\n")

    # Find all top-level functions and classes
    top_level_nodes = self._find_top_level_definitions(
        root_node, include_l2_in_file_skeleton=include_l2_in_skeleton
    )

    # Use relative_path for node_id generation, fallback to file_path
    path_for_node_id = relative_path if relative_path else file_path

    if self.chunk_depth == 0:
        if not lines:
            return []
        if use_skeleton:
            skeleton_content = self._build_file_skeleton(
                top_level_nodes, code_content
            )
            if not skeleton_content:
                return []
            return [
                CodeChunk(
                    skeleton_content,
                    0,
                    len(lines) - 1,
                    "file",
                    Path(file_path).name,
                    file_path,
                    path_for_node_id,
                )
            ]
        return self._split_by_max_lines(
            lines=lines,
            start_line=0,
            end_line=len(lines) - 1,
            chunk_type="file",
            name=Path(file_path).name,
            file_path=file_path,
            node_id=path_for_node_id,
        )

    # Generate chunks
    chunks = self._generate_chunks(
        lines=lines,
        definitions=top_level_nodes,
        file_path=file_path,
        path_for_node_id=path_for_node_id,
        code_content=code_content,
        skeleton_mode=use_skeleton,
    )

    # logger.debug(f"Generated {len(chunks)} chunks from {file_path}")
    return chunks

save_chunks_to_json

save_chunks_to_json(chunks: list[CodeChunk], output_path: str)

Save chunks to a JSON file.

Parameters:

Name Type Description Default
chunks list[CodeChunk]

List of CodeChunk objects

required
output_path str

Path to save the JSON file

required
Source code in codenib/code_chunking/base.py
def save_chunks_to_json(self, chunks: List[CodeChunk], output_path: str):
    """
    Save chunks to a JSON file.

    Args:
        chunks: List of CodeChunk objects
        output_path: Path to save the JSON file
    """
    # Convert chunks to dictionaries for JSON serialization
    chunk_dicts = [
        {
            "content": chunk.content,
            "start_line": chunk.start_line,
            "end_line": chunk.end_line,
            "chunk_type": chunk.chunk_type,
            "name": chunk.name,
            "file": chunk.file,
            "node_id": chunk.node_id,
        }
        for chunk in chunks
    ]

    try:
        with open(output_path, "w", encoding="utf-8") as f:
            json.dump(chunk_dicts, f, indent=2, ensure_ascii=False)
        logger.info(f"Chunks saved to {output_path}")
    except Exception as e:
        logger.error(f"Error saving chunks to file: {e}")

print_chunk_summary

print_chunk_summary(chunks: list[CodeChunk])

Print a summary of the generated chunks.

Source code in codenib/code_chunking/base.py
def print_chunk_summary(self, chunks: List[CodeChunk]):
    """Print a summary of the generated chunks."""
    logger.info(f"\n=== Chunk Summary ===")
    logger.info(f"Total chunks: {len(chunks)}")

    for i, chunk in enumerate(chunks, 1):
        logger.info(
            "Chunk %s: %s %r (lines %s-%s, %s lines)",
            i,
            chunk.chunk_type,
            chunk.name,
            chunk.start_line,
            chunk.end_line,
            len(chunk.content.split(chr(10))),
        )

CppCodeChunker

CppCodeChunker(
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker specifically for C++ files.

L1 entities: free functions and class/struct definitions. L2 entities: methods declared/defined inside class bodies. Skeleton mode: file skeleton lists L1 declarations and member signatures; class skeleton lists method signatures.

Source code in codenib/code_chunking/cpp_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    """Initialize the C++ code chunker."""
    super().__init__(
        "cpp",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

CSharpCodeChunker

CSharpCodeChunker(
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker for C# source files.

L1 entities: classes, interfaces, enums, records, structs, and top-level local functions. L2 entities: methods, constructors, and properties inside type declarations. Skeleton mode: file skeleton lists type declarations and member signatures.

Source code in codenib/code_chunking/csharp_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    super().__init__(
        "csharp",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

GoCodeChunker

GoCodeChunker(
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker for Go source files.

L1 entities: top-level functions and type declarations (struct/interface/type). L2 entities: methods with receivers. Skeleton mode: file skeleton lists L1 declarations and method signatures; type skeleton lists receiver-bound methods.

Source code in codenib/code_chunking/go_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    super().__init__(
        "go",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )
    self._methods_by_type: Dict[str, List[Tuple]] = {}

JavaCodeChunker

JavaCodeChunker(
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker for Java source files.

L1 entities: top-level classes, interfaces, enums, and records. L2 entities: methods and constructors inside those containers. Skeleton mode: file skeleton lists L1 declarations and member signatures.

Source code in codenib/code_chunking/java_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    super().__init__(
        "java",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

JsTsCodeChunker

JsTsCodeChunker(
    language: str = "javascript",
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Chunker for JavaScript and TypeScript source files.

L1 entities: top-level functions and classes. L2 entities: methods within classes. Skeleton mode: file skeleton lists L1 declarations and member signatures; class skeleton lists method signatures.

Source code in codenib/code_chunking/js_chunker.py
def __init__(
    self,
    language: str = "javascript",
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    # Always use the TypeScript parser: it is a strict superset of
    # JavaScript and also handles Flow-annotated (.js) files that the
    # JavaScript parser would reject.
    super().__init__(
        "typescript",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

KotlinCodeChunker

KotlinCodeChunker(
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker for Kotlin source files.

L1 entities: classes, interfaces, enums, objects, top-level functions, and top-level properties. L2 entities: functions, secondary constructors, and properties inside type/object declarations. Skeleton mode: file skeleton lists container declarations and member signatures.

Source code in codenib/code_chunking/kotlin_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    super().__init__(
        "kotlin",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

LuaCodeChunker

LuaCodeChunker(
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker for Lua source files.

Source code in codenib/code_chunking/lua_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    super().__init__(
        "lua",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

PhpCodeChunker

PhpCodeChunker(
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker for PHP source files.

L1 entities: classes, interfaces, traits, enums, and top-level functions. L2 entities: methods and properties inside type declarations. Skeleton mode: file skeleton lists type declarations and member signatures.

Source code in codenib/code_chunking/php_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    super().__init__(
        "php",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

PythonCodeChunker

PythonCodeChunker(
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker specifically for Python files.

L1 entities: module-level functions and classes. L2 entities: methods inside classes (including async and decorated definitions). Skeleton mode: module skeleton lists L1 definitions and class member signatures; class skeleton lists method signatures.

Source code in codenib/code_chunking/python_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    """Initialize the Python code chunker."""
    super().__init__(
        "python",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

RubyCodeChunker

RubyCodeChunker(
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker for Ruby source files.

L1 entities: top-level modules, classes, and methods. L2 entities: instance and singleton methods inside modules/classes. Skeleton mode: file skeleton lists namespaces and member signatures.

Source code in codenib/code_chunking/ruby_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    super().__init__(
        "ruby",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

RustCodeChunker

RustCodeChunker(
    max_lines_per_chunk: int | None = 200,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker for Rust source files.

L1 entities: free functions plus struct/enum/trait/impl blocks. L2 entities: functions inside impl blocks. Skeleton mode: file skeleton lists L1 declarations and impl member signatures; impl skeleton lists method signatures.

Source code in codenib/code_chunking/rust_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = 200,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    super().__init__(
        "rust",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

ScalaCodeChunker

ScalaCodeChunker(
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker for Scala source files.

Source code in codenib/code_chunking/scala_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    super().__init__(
        "scala",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

SwiftCodeChunker

SwiftCodeChunker(
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs
)

Bases: BaseCodeChunker

Code chunker for Swift source files.

Source code in codenib/code_chunking/swift_chunker.py
def __init__(
    self,
    max_lines_per_chunk: Optional[int] = None,
    chunk_depth: int = 2,
    l2_level_exclusive: bool = True,
    **kwargs,
):
    super().__init__(
        "swift",
        max_lines_per_chunk=max_lines_per_chunk,
        chunk_depth=chunk_depth,
        l2_level_exclusive=l2_level_exclusive,
        **kwargs,
    )

create_chunker

create_chunker(
    language: str,
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    include_header_epilogue: bool = False,
    l2_level_exclusive: bool = True,
    skeleton_mode: bool = False,
    include_l2_in_file_skeleton: bool = True,
) -> BaseCodeChunker

Create a code chunker for the specified language.

Parameters:

Name Type Description Default
language str

Programming language ('python', 'cpp', 'java', etc.)

required
max_lines_per_chunk int | None

Maximum number of lines per emitted chunk. Default: None (no splitting)

None
chunk_depth int

Granularity level 0 = Entire file as a chunk 1 = Top-level declarations only 2 = Include methods/impl members

2
include_header_epilogue bool

Whether to include file headers and epilogues. Default: False

False
l2_level_exclusive bool

When chunk_depth is 2, whether to omit L1 container nodes (classes/structs/impls) and emit only L2 members. Default: True.

True
skeleton_mode bool

Emit signature-only skeletons instead of full bodies when True.

False
include_l2_in_file_skeleton bool

When chunk_depth is 0, include member signatures in file-level skeletons for a hierarchical view. Default: True.

True

Returns:

Type Description
BaseCodeChunker

Language-specific code chunker instance

Raises:

Type Description
ValueError

If the language is not supported

Source code in codenib/code_chunking/__init__.py
def create_chunker(
    language: str,
    max_lines_per_chunk: int | None = None,
    chunk_depth: int = 2,
    include_header_epilogue: bool = False,
    l2_level_exclusive: bool = True,
    skeleton_mode: bool = False,
    include_l2_in_file_skeleton: bool = True,
) -> BaseCodeChunker:
    """
    Create a code chunker for the specified language.

    Args:
        language: Programming language ('python', 'cpp', 'java', etc.)
        max_lines_per_chunk: Maximum number of lines per emitted chunk. Default:
            None (no splitting)
        chunk_depth: Granularity level
            0 = Entire file as a chunk
            1 = Top-level declarations only
            2 = Include methods/impl members
        include_header_epilogue: Whether to include file headers and epilogues.
            Default: False
        l2_level_exclusive: When chunk_depth is 2, whether to omit L1 container
            nodes (classes/structs/impls) and emit only L2 members. Default: True.
        skeleton_mode: Emit signature-only skeletons instead of full bodies when True.
        include_l2_in_file_skeleton: When chunk_depth is 0, include member
            signatures in file-level skeletons for a hierarchical view. Default: True.

    Returns:
        Language-specific code chunker instance

    Raises:
        ValueError: If the language is not supported
    """
    spec = get_chunker_spec(language)
    if spec is None or spec.chunker_language is None or spec.chunker_class is None:
        supported = ", ".join(sorted(chunker_language_aliases()))
        raise ValueError(f"Unsupported language: {language}. Supported: {supported}")

    cls = _load_chunker_class(spec.chunker_class)
    kwargs = {
        "max_lines_per_chunk": max_lines_per_chunk,
        "chunk_depth": chunk_depth,
        "include_header_epilogue": include_header_epilogue,
        "l2_level_exclusive": l2_level_exclusive,
        "skeleton_mode": skeleton_mode,
        "include_l2_in_file_skeleton": include_l2_in_file_skeleton,
    }
    if spec.chunker_pass_language:
        kwargs["language"] = spec.chunker_language
    return cls(**kwargs)