Skip to content

cli

code_context_agent.cli

Command-line interface for code-context-agent.

This module provides the main CLI entry point using cyclopts.

Example

$ code-context-agent analyze /path/to/repo $ code-context-agent analyze /path/to/repo --focus "authentication" $ code-context-agent analyze . --quiet

main

main(debug=False, output_format='rich')

Run the code-context-agent CLI.

This is the main entry point for the CLI. It loads settings from the environment and displays a welcome message with current configuration.

Parameters:

Name Type Description Default
debug Annotated[bool, Parameter(help='Enable debug mode.')]

Enable debug output for verbose logging and diagnostics.

False
output_format Annotated[Literal['rich', 'json', 'plain'], Parameter(help='Output format: rich, json, plain.')]

Format for output display. Supported values are "rich" (default), "json", or "plain".

'rich'
Source code in packages/code-context-agent/src/code_context_agent/cli.py
@app.default
def main(
    debug: Annotated[bool, Parameter(help="Enable debug mode.")] = False,
    output_format: Annotated[
        Literal["rich", "json", "plain"],
        Parameter(help="Output format: rich, json, plain."),
    ] = "rich",
) -> None:
    """Run the code-context-agent CLI.

    This is the main entry point for the CLI. It loads settings from the
    environment and displays a welcome message with current configuration.

    Args:
        debug: Enable debug output for verbose logging and diagnostics.
        output_format: Format for output display. Supported values are
            "rich" (default), "json", or "plain".
    """
    settings = get_settings()
    if debug or output_format != "rich":
        settings = Settings(debug=debug, output_format=output_format)

    display_welcome(settings)

analyze

analyze(
    path=Path(),
    *,
    output_dir=None,
    focus="",
    output_format="rich",
    since="",
    full=False,
    quiet=False,
    quick=False,
    bundles_only=False,
    debug=False,
)

Analyze a codebase and produce targeted context bundles.

The coordinator dispatches parallel specialist teams that use GitNexus, ripgrep, repomix, and git history tools to understand the codebase, then consolidates findings into targeted bundle files.

Outputs

.code-context/bundles/BUNDLE.{area}.md - Targeted context bundles .code-context/heuristic_summary.json - Deterministic index summary

Example

$ code-context-agent analyze /path/to/repo $ code-context-agent analyze . --focus "authentication" $ code-context-agent analyze . --output-dir ./output $ code-context-agent analyze . --bundles-only

Source code in packages/code-context-agent/src/code_context_agent/cli.py
@app.command
def analyze(  # noqa: C901, PLR0912, PLR0915
    path: Annotated[
        Path,
        Parameter(help="Path to the repository to analyze."),
    ] = Path(),
    *,
    output_dir: Annotated[
        Path | None,
        Parameter(help="Output directory for context files. Defaults to <repo>/.code-context"),
    ] = None,
    focus: Annotated[
        str,
        Parameter(help="Focus area for analysis (e.g., 'authentication', 'API endpoints', 'database layer')."),
    ] = "",
    output_format: Annotated[
        Literal["rich", "json"],
        Parameter(help="Output format: rich (default TUI), json (AnalysisResult JSON to stdout)."),
    ] = "rich",
    since: Annotated[
        str,
        Parameter(
            help="Git ref for incremental analysis (e.g., 'HEAD~5', 'main', 'abc123'). "
            "Only re-analyzes files changed since this ref.",
        ),
    ] = "",
    full: Annotated[
        bool,
        Parameter(help="Run exhaustive analysis with no size limits and fail-fast error handling."),
    ] = False,
    quiet: Annotated[
        bool,
        Parameter(help="Suppress all output except errors on stderr. No TUI, no JSON."),
    ] = False,
    quick: Annotated[
        bool,
        Parameter(help="Lightweight analysis: single scout wave, risk profile refresh only. For nightly CI/CD."),
    ] = False,
    bundles_only: Annotated[
        bool,
        Parameter(help="Skip indexing and team dispatch; regenerate bundles from existing team findings."),
    ] = False,
    debug: Annotated[
        bool,
        Parameter(help="Enable debug logging for troubleshooting."),
    ] = False,
) -> None:
    """Analyze a codebase and produce targeted context bundles.

    The coordinator dispatches parallel specialist teams that use GitNexus,
    ripgrep, repomix, and git history tools to understand the codebase,
    then consolidates findings into targeted bundle files.

    Outputs:
        .code-context/bundles/BUNDLE.{area}.md - Targeted context bundles
        .code-context/heuristic_summary.json - Deterministic index summary

    Example:
        $ code-context-agent analyze /path/to/repo
        $ code-context-agent analyze . --focus "authentication"
        $ code-context-agent analyze . --output-dir ./output
        $ code-context-agent analyze . --bundles-only
    """
    import sys

    from loguru import logger

    from code_context_agent.usecase import CodebaseAnalysisUseCase
    from code_context_agent.utils import setup_logger
    from strands_teams import run_teams_sync

    # Configure logging: suppress loguru in normal mode (Rich Live conflict),
    # enable full logging in debug mode (which uses QuietConsumer instead)
    if debug:
        setup_logger(level="DEBUG")
        if not quiet:
            console.print("[dim]Debug logging enabled (live display disabled)[/dim]")
    else:
        # Remove default loguru handler to prevent stderr writes that break Rich Live
        logger.remove()
        setup_logger(level="WARNING")

    repo_path = path.resolve()

    if not repo_path.exists():
        print(f"Error: Path does not exist: {repo_path}", file=sys.stderr)
        raise SystemExit(1)

    if not repo_path.is_dir():
        print(f"Error: Path is not a directory: {repo_path}", file=sys.stderr)
        raise SystemExit(1)

    # Validate flag combinations
    _validate_flags(full=full, since=since, bundles_only=bundles_only, quick=quick)

    # Derive analysis mode
    mode = _derive_mode(full=full, focus=focus, since=since, quick=quick)

    # Auto-preflight in full mode
    if full and not quiet:
        preflight = _preflight_check()
        missing = [name for name, info in preflight.items() if not info["available"]]
        if missing:
            console.print(f"[yellow]Warning:[/yellow] Missing tools: {', '.join(missing)}")
            console.print("  Full mode works best with all tools installed.")

    # Show analysis configuration
    if not quiet:
        console.print()
        console.print("[bold]Code Context Analysis[/bold]")
        console.print(f"  Repository: [cyan]{repo_path}[/cyan]")
        if focus:
            console.print(f"  Focus: [magenta]{focus}[/magenta]")
        if full:
            console.print("  Mode: [bold magenta]FULL (exhaustive)[/bold magenta]")
        if bundles_only:
            console.print("  Mode: [bold magenta]BUNDLES ONLY (regenerate from findings)[/bold magenta]")
        console.print()

    # In debug mode or JSON mode, use quiet consumer (log output replaces Live display)
    use_quiet = quiet or debug or (output_format == "json")

    # Build incremental analysis context if --since provided
    since_context = None
    if since:
        effective_output = output_dir or repo_path / DEFAULT_OUTPUT_DIR
        since_context = _build_since_context(repo_path, since, effective_output)
    if since_context and not use_quiet:
        console.print(f"  Incremental: [magenta]since {since}[/magenta]")

    # Build the use case, binding the layout to the requested output directory.
    output_root = (output_dir or repo_path / DEFAULT_OUTPUT_DIR).resolve()
    use_case = CodebaseAnalysisUseCase(output_root=output_root)

    # Run the analysis through the strands_teams runner.
    result = run_teams_sync(
        use_case,
        repo_path=repo_path,
        focus=focus or None,
        extra_context=since_context,
        bundles_only=bundles_only,
        full_mode=mode in ("full", "full+focus"),
        quiet=use_quiet,
    )
    # Bridge the runner's result keys to the CLI display contract: the runner
    # names the executive-summary path ``executive_summary_path`` (CONTEXT.md),
    # while ``_display_result*`` expect ``context_path``.
    result["context_path"] = result.pop("executive_summary_path", None)

    if output_format == "json":
        _display_result_json(result)
    else:
        _display_result(result, debug=debug, quiet=quiet)

index

index(path=Path(), *, output_dir=None, quiet=False)

Build a deterministic index without LLM calls.

Faster and cheaper than full analysis. Runs GitNexus indexing, git analysis, and static scanners to produce artifacts for agent analysis.

Example

$ code-context-agent index /path/to/repo $ code-context-agent index . --output-dir ./output $ code-context-agent index . --quiet

Source code in packages/code-context-agent/src/code_context_agent/cli.py
@app.command
def index(
    path: Annotated[
        Path,
        Parameter(help="Path to the repository to index."),
    ] = Path(),
    *,
    output_dir: Annotated[
        Path | None,
        Parameter(help="Output directory for the code graph. Defaults to <repo>/.code-context"),
    ] = None,
    quiet: Annotated[
        bool,
        Parameter(help="Suppress all output except errors."),
    ] = False,
) -> None:
    """Build a deterministic index without LLM calls.

    Faster and cheaper than full analysis. Runs GitNexus indexing, git
    analysis, and static scanners to produce artifacts for agent analysis.

    Example:
        $ code-context-agent index /path/to/repo
        $ code-context-agent index . --output-dir ./output
        $ code-context-agent index . --quiet
    """
    import asyncio
    import sys

    from code_context_agent.indexer import build_index

    repo_path = path.resolve()

    if not repo_path.exists():
        print(f"Error: Path does not exist: {repo_path}", file=sys.stderr)
        raise SystemExit(1)

    if not repo_path.is_dir():
        print(f"Error: Path is not a directory: {repo_path}", file=sys.stderr)
        raise SystemExit(1)

    asyncio.run(build_index(repo_path, output_dir, quiet))

check

check()

Check availability of external tool dependencies and AWS credentials.

Verifies that all CLI tools used by the indexer and analysis pipeline are installed, and that AWS credentials are configured for Bedrock access.

Example

$ code-context-agent check

Source code in packages/code-context-agent/src/code_context_agent/cli.py
@app.command
def check() -> None:
    """Check availability of external tool dependencies and AWS credentials.

    Verifies that all CLI tools used by the indexer and analysis pipeline
    are installed, and that AWS credentials are configured for Bedrock access.

    Example:
        $ code-context-agent check
    """
    preflight = _preflight_check()
    groups = [
        ("Core tools (required)", "core", True),
        ("Static analysis (optional, used by indexer)", "analysis", False),
        ("Security scanners (optional)", "security", False),
    ]

    all_ok = True
    required_ok = True
    for label, group, is_required in groups:
        group_ok = _print_tool_group(preflight, label, group, is_required)
        if not group_ok:
            all_ok = False
            if is_required:
                required_ok = False

    console.print("\n[bold]AWS credentials:[/bold]")
    if _check_aws_credentials():
        console.print("  [green]\u2713[/green] sts get-caller-identity")
    else:
        console.print("  [red]\u2717[/red] sts get-caller-identity \u2014 configure AWS credentials for Bedrock")
        all_ok = False

    if all_ok:
        console.print("\n[green]All tools and credentials available.[/green]")
    elif required_ok:
        console.print("\n[yellow]Some optional tools are missing. Core analysis will work.[/yellow]")
    else:
        console.print("\n[red]Required tools are missing. Analysis will not work.[/red]")
        raise SystemExit(1)