Skip to content

Commit 819d7b7

Browse files
jeremyederclaude
andauthored
feat: Implement IssuePRTemplatesAssessor (fixes #84) (#90)
Add assessor for GitHub issue and PR templates (Tier 3). **Scoring Logic** (pass threshold: 75): - PR template exists: 50% - Issue templates exist (≥2): 50% **Implementation**: - Checks for PR template in .github/PULL_REQUEST_TEMPLATE.md (multiple locations) - Checks for ISSUE_TEMPLATE directory - Counts both .md and .yml/.yaml templates - Requires minimum 2 issue templates for full score **Self-Assessment**: 100/100 (pass) - PR template found - Issue templates found: 3 templates 13/27 attributes now implemented 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9495c44 commit 819d7b7

File tree

2 files changed

+176
-2
lines changed

2 files changed

+176
-2
lines changed

src/agentready/assessors/structure.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,172 @@ def _create_remediation(self) -> Remediation:
307307
),
308308
],
309309
)
310+
311+
312+
class IssuePRTemplatesAssessor(BaseAssessor):
313+
"""Assesses presence of GitHub issue and PR templates.
314+
315+
Tier 3 Important (1.5% weight) - Templates provide structure for AI
316+
when creating issues/PRs and ensure consistent formatting.
317+
"""
318+
319+
@property
320+
def attribute_id(self) -> str:
321+
return "issue_pr_templates"
322+
323+
@property
324+
def tier(self) -> int:
325+
return 3 # Important
326+
327+
@property
328+
def attribute(self) -> Attribute:
329+
return Attribute(
330+
id=self.attribute_id,
331+
name="Issue & Pull Request Templates",
332+
category="Repository Structure",
333+
tier=self.tier,
334+
description="Standardized templates for issues and PRs",
335+
criteria="PR template and issue templates in .github/",
336+
default_weight=0.015,
337+
)
338+
339+
def assess(self, repository: Repository) -> Finding:
340+
"""Check for GitHub issue and PR templates.
341+
342+
Scoring:
343+
- PR template exists (50%)
344+
- Issue templates exist (50%, requires ≥2 templates)
345+
"""
346+
score = 0
347+
evidence = []
348+
349+
# Check for PR template (50%)
350+
pr_template_paths = [
351+
repository.path / ".github" / "PULL_REQUEST_TEMPLATE.md",
352+
repository.path / "PULL_REQUEST_TEMPLATE.md",
353+
repository.path / ".github" / "pull_request_template.md",
354+
]
355+
356+
pr_template_found = any(p.exists() for p in pr_template_paths)
357+
358+
if pr_template_found:
359+
score += 50
360+
evidence.append("PR template found")
361+
else:
362+
evidence.append("No PR template found")
363+
364+
# Check for issue templates (50%)
365+
issue_template_dir = repository.path / ".github" / "ISSUE_TEMPLATE"
366+
367+
if issue_template_dir.exists() and issue_template_dir.is_dir():
368+
try:
369+
# Count .md and .yml files (both formats supported)
370+
md_templates = list(issue_template_dir.glob("*.md"))
371+
yml_templates = list(issue_template_dir.glob("*.yml")) + list(
372+
issue_template_dir.glob("*.yaml")
373+
)
374+
template_count = len(md_templates) + len(yml_templates)
375+
376+
if template_count >= 2:
377+
score += 50
378+
evidence.append(
379+
f"Issue templates found: {template_count} templates"
380+
)
381+
elif template_count == 1:
382+
score += 25
383+
evidence.append(
384+
"Issue template directory exists with 1 template (need ≥2)"
385+
)
386+
else:
387+
evidence.append("Issue template directory exists but is empty")
388+
except OSError:
389+
evidence.append("Could not read issue template directory")
390+
else:
391+
evidence.append("No issue template directory found")
392+
393+
status = "pass" if score >= 75 else "fail"
394+
395+
return Finding(
396+
attribute=self.attribute,
397+
status=status,
398+
score=score,
399+
measured_value=f"PR:{pr_template_found}, Issues:{template_count if issue_template_dir.exists() else 0}",
400+
threshold="PR template + ≥2 issue templates",
401+
evidence=evidence,
402+
remediation=self._create_remediation() if status == "fail" else None,
403+
error_message=None,
404+
)
405+
406+
def _create_remediation(self) -> Remediation:
407+
"""Create remediation guidance for missing templates."""
408+
return Remediation(
409+
summary="Create GitHub issue and PR templates in .github/ directory",
410+
steps=[
411+
"Create .github/ directory if it doesn't exist",
412+
"Add PULL_REQUEST_TEMPLATE.md for PRs",
413+
"Create .github/ISSUE_TEMPLATE/ directory",
414+
"Add bug_report.md for bug reports",
415+
"Add feature_request.md for feature requests",
416+
"Optionally add config.yml to configure template chooser",
417+
],
418+
tools=["gh"],
419+
commands=[
420+
"# Create directories",
421+
"mkdir -p .github/ISSUE_TEMPLATE",
422+
"",
423+
"# Create PR template",
424+
"cat > .github/PULL_REQUEST_TEMPLATE.md << 'EOF'",
425+
"## Summary",
426+
"<!-- Describe the changes in this PR -->",
427+
"",
428+
"## Related Issues",
429+
"Fixes #",
430+
"",
431+
"## Testing",
432+
"- [ ] Tests added/updated",
433+
"- [ ] All tests pass",
434+
"",
435+
"## Checklist",
436+
"- [ ] Documentation updated",
437+
"- [ ] CHANGELOG.md updated",
438+
"EOF",
439+
],
440+
examples=[
441+
"""# Bug Report Template (.github/ISSUE_TEMPLATE/bug_report.md)
442+
443+
```markdown
444+
---
445+
name: Bug Report
446+
about: Create a report to help us improve
447+
title: '[BUG] '
448+
labels: bug
449+
assignees: ''
450+
---
451+
452+
**Describe the bug**
453+
A clear description of what the bug is.
454+
455+
**To Reproduce**
456+
Steps to reproduce:
457+
1. Go to '...'
458+
2. Click on '....'
459+
3. See error
460+
461+
**Expected behavior**
462+
What you expected to happen.
463+
464+
**Environment**
465+
- OS: [e.g. macOS 13.0]
466+
- Version: [e.g. 1.0.0]
467+
```
468+
""",
469+
],
470+
citations=[
471+
Citation(
472+
source="GitHub Docs",
473+
title="About issue and pull request templates",
474+
url="https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates",
475+
relevance="Official GitHub guide for templates",
476+
),
477+
],
478+
)

src/agentready/cli/main.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
CLAUDEmdAssessor,
2424
READMEAssessor,
2525
)
26-
from ..assessors.structure import OneCommandSetupAssessor, StandardLayoutAssessor
26+
from ..assessors.structure import (
27+
IssuePRTemplatesAssessor,
28+
OneCommandSetupAssessor,
29+
StandardLayoutAssessor,
30+
)
2731
from ..assessors.stub_assessors import (
2832
ConventionalCommitsAssessor,
2933
GitignoreAssessor,
@@ -74,8 +78,9 @@ def create_all_assessors():
7478
GitignoreAssessor(),
7579
OneCommandSetupAssessor(),
7680
CyclomaticComplexityAssessor(), # Actually Tier 3, but including here
77-
# Tier 3 Important (1 implemented)
81+
# Tier 3 Important (2 implemented)
7882
ArchitectureDecisionsAssessor(),
83+
IssuePRTemplatesAssessor(),
7984
]
8085

8186
# Add remaining stub assessors

0 commit comments

Comments
 (0)