Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55,755 changes: 55,755 additions & 0 deletions reports/repomix/repomix-output.md

Large diffs are not rendered by default.

12,321 changes: 12,321 additions & 0 deletions reports/repomix/repomix-output.xml

Large diffs are not rendered by default.

16 changes: 11 additions & 5 deletions src/agentready/assessors/code_quality.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
"""Code quality assessors for complexity, file length, type annotations, and code smells."""

import ast
import subprocess
import logging

from ..models.attribute import Attribute
from ..models.finding import Citation, Finding, Remediation
from ..models.repository import Repository
from ..services.scanner import MissingToolError
from ..utils.subprocess_utils import safe_subprocess_run
from .base import BaseAssessor

logger = logging.getLogger(__name__)


class TypeAnnotationsAssessor(BaseAssessor):
"""Assesses type annotation coverage in code.
Expand Down Expand Up @@ -71,7 +74,8 @@ def _assess_python_types(self, repository: Repository) -> Finding:
"""Assess Python type annotations using AST parsing."""
# Use AST parsing to accurately detect type annotations
try:
result = subprocess.run(
# Security: Use safe_subprocess_run for validation and limits
result = safe_subprocess_run(
["git", "ls-files", "*.py"],
cwd=repository.path,
capture_output=True,
Expand All @@ -80,7 +84,7 @@ def _assess_python_types(self, repository: Repository) -> Finding:
check=True,
)
python_files = [f for f in result.stdout.strip().split("\n") if f]
except (subprocess.SubprocessError, FileNotFoundError):
except Exception:
python_files = [
str(f.relative_to(repository.path))
for f in repository.path.rglob("*.py")
Expand Down Expand Up @@ -295,7 +299,8 @@ def _assess_python_complexity(self, repository: Repository) -> Finding:
"""Assess Python complexity using radon."""
try:
# Check if radon is available
result = subprocess.run(
# Security: Use safe_subprocess_run for validation and limits
result = safe_subprocess_run(
["radon", "cc", str(repository.path), "-s", "-a"],
capture_output=True,
text=True,
Expand Down Expand Up @@ -351,7 +356,8 @@ def _assess_python_complexity(self, repository: Repository) -> Finding:
def _assess_with_lizard(self, repository: Repository) -> Finding:
"""Assess complexity using lizard (multi-language)."""
try:
result = subprocess.run(
# Security: Use safe_subprocess_run for validation and limits
result = safe_subprocess_run(
["lizard", str(repository.path)],
capture_output=True,
text=True,
Expand Down
8 changes: 4 additions & 4 deletions src/agentready/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from ..reporters.markdown import MarkdownReporter
from ..services.research_loader import ResearchLoader
from ..services.scanner import Scanner
from ..utils.subprocess_utils import safe_subprocess_run
from .align import align
from .bootstrap import bootstrap
from .demo import demo
Expand Down Expand Up @@ -151,9 +152,8 @@ def run_assessment(repository_path, verbose, output_dir, config_path):
# Performance: Warn for large repositories
try:
# Quick file count using git ls-files (if it's a git repo) or fallback
import subprocess

result = subprocess.run(
# Security: Use safe_subprocess_run for validation and limits
result = safe_subprocess_run(
["git", "ls-files"],
cwd=repo_path,
capture_output=True,
Expand All @@ -172,7 +172,7 @@ def run_assessment(repository_path, verbose, output_dir, config_path):
"Assessment may take several minutes. Continue?",
abort=True,
)
except (subprocess.TimeoutExpired, Exception):
except Exception:
# If we can't count files quickly, just continue
pass

Expand Down
21 changes: 14 additions & 7 deletions src/agentready/services/language_detector.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
"""Language detection service using file extension analysis."""

import subprocess
import logging
from collections import defaultdict
from pathlib import Path

from ..utils.subprocess_utils import safe_subprocess_run

logger = logging.getLogger(__name__)


class LanguageDetector:
"""Detects programming languages in a repository.
Expand Down Expand Up @@ -75,7 +79,8 @@ def detect_languages(self) -> dict[str, int]:

# Try git ls-files first (respects .gitignore)
try:
result = subprocess.run(
# Security: Use safe_subprocess_run for validation and limits
result = safe_subprocess_run(
["git", "ls-files"],
cwd=self.repository_path,
capture_output=True,
Expand All @@ -84,7 +89,7 @@ def detect_languages(self) -> dict[str, int]:
check=True,
)
files = result.stdout.strip().split("\n")
except (subprocess.SubprocessError, FileNotFoundError):
except Exception:
# Fall back to pathlib walk (less accurate)
files = [
str(f.relative_to(self.repository_path))
Expand Down Expand Up @@ -118,7 +123,8 @@ def count_total_files(self) -> int:
Total file count
"""
try:
result = subprocess.run(
# Security: Use safe_subprocess_run for validation and limits
result = safe_subprocess_run(
["git", "ls-files"],
cwd=self.repository_path,
capture_output=True,
Expand All @@ -128,7 +134,7 @@ def count_total_files(self) -> int:
)
files = result.stdout.strip().split("\n")
return len([f for f in files if f.strip()])
except (subprocess.SubprocessError, FileNotFoundError):
except Exception:
# Fall back to pathlib
return sum(1 for _ in self.repository_path.rglob("*") if _.is_file())

Expand All @@ -144,7 +150,8 @@ def count_total_lines(self) -> int:
total_lines = 0

try:
result = subprocess.run(
# Security: Use safe_subprocess_run for validation and limits
result = safe_subprocess_run(
["git", "ls-files"],
cwd=self.repository_path,
capture_output=True,
Expand All @@ -153,7 +160,7 @@ def count_total_lines(self) -> int:
check=True,
)
files = result.stdout.strip().split("\n")
except (subprocess.SubprocessError, FileNotFoundError):
except Exception:
files = [
str(f.relative_to(self.repository_path))
for f in self.repository_path.rglob("*")
Expand Down
22 changes: 18 additions & 4 deletions src/agentready/services/repomix.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
"""Repomix integration service for generating AI-friendly repository context."""

import json
import logging
import shutil
import subprocess
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Tuple

from ..utils.subprocess_utils import safe_subprocess_run, sanitize_subprocess_error

logger = logging.getLogger(__name__)


class RepomixService:
"""Service for managing Repomix configuration and generation."""
Expand Down Expand Up @@ -214,8 +218,15 @@ def run_repomix(
cmd.append("--verbose")

try:
result = subprocess.run(
cmd, cwd=self.repo_path, capture_output=True, text=True, check=False
# Security: Use safe_subprocess_run with extended timeout for Repomix
# Repomix can be slow on large repos, allow 5 minutes
result = safe_subprocess_run(
cmd,
cwd=self.repo_path,
capture_output=True,
text=True,
check=False,
timeout=300, # 5 minutes for large repos
)

if result.returncode == 0:
Expand All @@ -225,7 +236,10 @@ def run_repomix(
return False, f"Repomix failed: {error_msg}"

except Exception as e:
return False, f"Error running repomix: {str(e)}"
# Security: Sanitize error messages
safe_msg = sanitize_subprocess_error(e, self.repo_path)
logger.error(f"Repomix error: {safe_msg}")
return False, f"Error running repomix: {safe_msg}"

def get_output_files(self) -> List[Path]:
"""Get list of existing Repomix output files.
Expand Down
17 changes: 17 additions & 0 deletions src/agentready/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Utility modules for AgentReady."""

from .subprocess_utils import (
SUBPROCESS_TIMEOUT,
SubprocessSecurityError,
safe_subprocess_run,
sanitize_subprocess_error,
validate_repository_path,
)

__all__ = [
"safe_subprocess_run",
"sanitize_subprocess_error",
"validate_repository_path",
"SubprocessSecurityError",
"SUBPROCESS_TIMEOUT",
]
Loading
Loading