Skip to content

Commit 77efebe

Browse files
jwm4claude
authored andcommitted
fix: case-insensitive file lookups and GNUmakefile support
- READMEAssessor: find README.md/rst/txt case-insensitively via iterdir() instead of hardcoded path (closes #387) - OneCommandSetupAssessor: recognize GNUmakefile and makefile in addition to Makefile (closes #380) - ArchitectureDecisionsAssessor: scan docs/ and repo root for ADR directories case-insensitively (closes #379) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 947c913 commit 77efebe

2 files changed

Lines changed: 52 additions & 31 deletions

File tree

src/agentready/assessors/documentation.py

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,29 @@ def assess(self, repository: Repository) -> Finding:
400400
Pass criteria: README.md exists with essential sections
401401
Scoring: Proportional based on section count
402402
"""
403-
readme_path = repository.path / "README.md"
403+
# Case-insensitive README lookup — handles readme.md, README.md, Readme.rst, etc.
404+
readme_names = {"readme.md", "readme.rst", "readme.txt", "readme"}
405+
readme_path = next(
406+
(
407+
f
408+
for f in repository.path.iterdir()
409+
if f.is_file() and f.name.lower() in readme_names
410+
),
411+
None,
412+
)
413+
414+
if readme_path is None:
415+
return Finding(
416+
attribute=self.attribute,
417+
status="fail",
418+
score=0.0,
419+
measured_value="missing",
420+
threshold="present with sections",
421+
evidence=["README not found"],
422+
remediation=self._create_remediation(),
423+
error_message=None,
424+
)
404425

405-
# Fix TOCTOU: Use try-except around file read instead of existence check
406426
try:
407427
with open(readme_path, "r", encoding="utf-8") as f:
408428
content = f.read().lower()
@@ -450,20 +470,9 @@ def assess(self, repository: Repository) -> Finding:
450470
error_message=None,
451471
)
452472

453-
except FileNotFoundError:
454-
return Finding(
455-
attribute=self.attribute,
456-
status="fail",
457-
score=0.0,
458-
measured_value="missing",
459-
threshold="present with sections",
460-
evidence=["README.md not found"],
461-
remediation=self._create_remediation(),
462-
error_message=None,
463-
)
464473
except OSError as e:
465474
return Finding.error(
466-
self.attribute, reason=f"Could not read README.md: {str(e)}"
475+
self.attribute, reason=f"Could not read {readme_path.name}: {str(e)}"
467476
)
468477

469478
def _create_remediation(self) -> Remediation:
@@ -551,23 +560,33 @@ def assess(self, repository: Repository) -> Finding:
551560
- ADR count (40%, up to 5 ADRs)
552561
- Template compliance (20%)
553562
"""
554-
# Check for ADR directory in common locations
555-
adr_paths = [
556-
repository.path / "docs" / "adr",
557-
repository.path / ".adr",
558-
repository.path / "adr",
559-
repository.path / "docs" / "decisions",
560-
repository.path / "specs",
561-
repository.path / "docs" / "specs",
562-
repository.path / "docs" / "architecture",
563-
repository.path / "docs" / "design",
564-
]
565-
563+
# Case-insensitive ADR directory scan — handles docs/adr, docs/ADRs, docs/Adr, adr/, etc.
564+
adr_target_names = {
565+
"adr", "adrs", "decisions", "architecture-decisions",
566+
"specs", "architecture", "design",
567+
}
566568
adr_dir = None
567-
for path in adr_paths:
568-
if path.exists() and path.is_dir():
569-
adr_dir = path
570-
break
569+
570+
# Search docs/ first (most common location)
571+
docs_dir = repository.path / "docs"
572+
if docs_dir.is_dir():
573+
for candidate in sorted(docs_dir.iterdir()):
574+
if candidate.is_dir() and candidate.name.lower() in adr_target_names:
575+
adr_dir = candidate
576+
break
577+
578+
# Fall back to repo root and hidden .adr
579+
if not adr_dir:
580+
if (repository.path / ".adr").is_dir():
581+
adr_dir = repository.path / ".adr"
582+
else:
583+
for candidate in sorted(repository.path.iterdir()):
584+
if (
585+
candidate.is_dir()
586+
and candidate.name.lower() in adr_target_names
587+
):
588+
adr_dir = candidate
589+
break
571590

572591
if not adr_dir:
573592
return Finding(
@@ -577,7 +596,7 @@ def assess(self, repository: Repository) -> Finding:
577596
measured_value="no ADR directory",
578597
threshold="ADR directory with decisions",
579598
evidence=[
580-
"No ADR directory found (checked docs/adr/, .adr/, adr/, docs/decisions/, specs/, docs/specs/, docs/architecture/, docs/design/)"
599+
"No ADR directory found (checked docs/adr/, docs/ADRs/, adr/, specs/, architecture/, design/, and variants)"
581600
],
582601
remediation=self._create_remediation(),
583602
error_message=None,

src/agentready/assessors/structure.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,8 @@ def _check_setup_files(self, repository: Repository) -> list:
531531
# Check for common setup files
532532
files_to_check = {
533533
"Makefile": "Makefile",
534+
"GNUmakefile": "GNUmakefile",
535+
"makefile": "Makefile",
534536
"setup.sh": "shell script",
535537
"bootstrap.sh": "bootstrap script",
536538
"package.json": "npm/yarn",

0 commit comments

Comments
 (0)