Add codechunk package files
Some checks failed
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled

This commit is contained in:
2026-02-01 23:42:00 +00:00
parent 7f6856ef07
commit fce6fb3fec

170
codechunk/cli.py Normal file
View File

@@ -0,0 +1,170 @@
import os
from pathlib import Path
from typing import Optional, List
import click
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
from codechunk.config import Config, load_config
from codechunk.core.chunking import CodeChunker
from codechunk.core.parser import CodeParser
from codechunk.core.formatter import OutputFormatter
from codechunk.core.dependency import DependencyAnalyzer
from codechunk.utils.logger import get_logger
console = Console()
logger = get_logger(__name__)
@click.group()
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose output")
@click.option("--config", "-c", type=click.Path(), help="Path to config file")
@click.pass_context
def main(ctx: click.Context, verbose: bool, config: Optional[str]) -> None:
ctx.ensure_object(dict)
ctx.obj["verbose"] = verbose
ctx.obj["config_path"] = config
if verbose:
logger.setLevel("DEBUG")
@main.command()
@click.argument("path", type=click.Path(exists=True, file_okay=False, dir_okay=True))
@click.option("--output", "-o", type=click.Path(), help="Output file path")
@click.option("--format", "-f", type=click.Choice(["ollama", "lmstudio", "markdown"]), default="markdown",
help="Output format")
@click.option("--max-tokens", "-t", type=int, default=8192, help="Maximum token limit")
@click.option("--include", multiple=True, help="File patterns to include")
@click.option("--exclude", multiple=True, help="File patterns to exclude")
@click.pass_context
def generate(ctx: click.Context, path: str, output: Optional[str], format: str,
max_tokens: int, include: tuple, exclude: tuple) -> None:
"""Generate optimized context bundle for LLM."""
config_path = ctx.obj.get("config_path")
verbose = ctx.obj.get("verbose", False)
try:
config = load_config(config_path) if config_path else Config()
if include:
config.chunking.include_patterns = list(include)
if exclude:
config.chunking.exclude_patterns = list(exclude)
project_path = Path(path)
logger.info(f"Analyzing project: {project_path}")
parser = CodeParser()
parser.discover_files(project_path, config.chunking.include_patterns,
config.chunking.exclude_patterns)
if verbose:
console.print(f"[cyan]Found {len(parser.files)} files to process[/cyan]")
chunks = parser.parse_all()
chunker = CodeChunker(config.chunking)
chunks = chunker.chunk_all(chunks)
analyzer = DependencyAnalyzer()
analyzer.analyze_dependencies(chunks, parser.files)
formatter = OutputFormatter(format, max_tokens)
formatted_output = formatter.format(chunks)
if output:
output_path = Path(output)
output_path.write_text(formatted_output)
console.print(f"[green]Context written to: {output_path}[/green]")
else:
console.print(Panel(formatted_output, title="Generated Context", expand=False))
console.print(f"\n[cyan]Summary:[/cyan]")
console.print(f" - Files processed: {len(parser.files)}")
console.print(f" - Chunks generated: {len(chunks)}")
console.print(f" - Estimated tokens: {formatter.estimate_tokens(formatted_output)}")
except Exception as e:
logger.error(f"Error generating context: {e}")
console.print(f"[red]Error: {e}[/red]")
raise click.Abort()
@main.command()
@click.argument("path", type=click.Path(exists=True, file_okay=False, dir_okay=True))
@click.option("--json", is_flag=True, help="Output in JSON format")
@click.pass_context
def analyze(ctx: click.Context, path: str, json: bool) -> None:
"""Analyze codebase and report statistics."""
verbose = ctx.obj.get("verbose", False)
try:
project_path = Path(path)
logger.info(f"Analyzing project: {project_path}")
config = Config()
parser = CodeParser()
parser.discover_files(project_path, config.chunking.include_patterns,
config.chunking.exclude_patterns)
chunks = parser.parse_all()
chunker = CodeChunker(config.chunking)
chunks = chunker.chunk_all(chunks)
stats = {
"total_files": len(parser.files),
"total_chunks": len(chunks),
"files_by_language": {},
"chunks_by_type": {},
"total_lines": sum(c.metadata.line_count for c in chunks),
"total_functions": sum(1 for c in chunks if c.chunk_type == "function"),
"total_classes": sum(1 for c in chunks if c.chunk_type == "class"),
}
for chunk in chunks:
lang = chunk.metadata.language
stats["files_by_language"][lang] = stats["files_by_language"].get(lang, 0) + 1
stats["chunks_by_type"][chunk.chunk_type] = stats["chunks_by_type"].get(chunk.chunk_type, 0) + 1
if json:
import json as json_module
console.print(json_module.dumps(stats, indent=2))
else:
console.print(Panel(
Text.from_markup(f"""
[b]Project Analysis[/b]
Total Files: {stats['total_files']}
Total Chunks: {stats['total_chunks']}
Total Lines: {stats['total_lines']}
Total Functions: {stats['total_functions']}
Total Classes: {stats['total_classes']}
[b]Files by Language[/b]
{chr(10).join(f' - {lang}: {count}' for lang, count in stats['files_by_language'].items())}
[b]Chunks by Type[/b]
{chr(10).join(f' - {type_}: {count}' for type_, count in stats['chunks_by_type'].items())}
"""),
title="Analysis Results",
expand=False
))
except Exception as e:
logger.error(f"Error analyzing project: {e}")
console.print(f"[red]Error: {e}[/red]")
raise click.Abort()
@main.command()
@click.pass_context
def version(ctx: click.Context) -> None:
"""Show version information."""
from codechunk import __version__
console.print(f"[cyan]CodeChunk CLI v{__version__}[/cyan]")
if __name__ == "__main__":
main()