Skip to content

Commit 80425cb

Browse files
jeremyederclaude
andcommitted
feat: Complete Repomix integration with bootstrap --repomix flag
This commit implements issue #8 by adding comprehensive Repomix integration: - Added RepomixConfigAssessor to assess Repomix configuration and freshness - Added --repomix flag to bootstrap command to optionally enable Repomix - Integrated RepomixService into BootstrapGenerator for setup automation - Created GitHub Actions workflow template for automatic Repomix updates - Added comprehensive integration and unit tests for Repomix functionality - Fixed Jinja2 template escaping issues in repomix-update.yml workflow - Updated Finding constructors in RepomixConfigAssessor to match API - Fixed unused imports across multiple files (ruff auto-fix) The --repomix flag allows users to optionally include Repomix configuration when bootstrapping a repository: agentready bootstrap --repomix This creates: - repomix.config.json (Repomix configuration) - .repomixignore (ignore patterns) - .github/workflows/repomix-update.yml (auto-update workflow) All tests passing (95/95). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent fa8016c commit 80425cb

File tree

17 files changed

+285
-69
lines changed

17 files changed

+285
-69
lines changed

src/agentready/assessors/code_quality.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"""Code quality assessors for complexity, file length, type annotations, and code smells."""
22

33
import subprocess
4-
from pathlib import Path
54

65
from ..models.attribute import Attribute
76
from ..models.finding import Citation, Finding, Remediation
@@ -326,7 +325,7 @@ def _assess_python_complexity(self, repository: Repository) -> Finding:
326325
self.attribute, reason="No Python code to analyze"
327326
)
328327

329-
except MissingToolError as e:
328+
except MissingToolError:
330329
raise # Re-raise to be caught by Scanner
331330
except Exception as e:
332331
return Finding.error(
@@ -352,7 +351,7 @@ def _assess_with_lizard(self, repository: Repository) -> Finding:
352351
self.attribute, reason="Lizard analysis not fully implemented"
353352
)
354353

355-
except MissingToolError as e:
354+
except MissingToolError:
356355
raise
357356
except Exception as e:
358357
return Finding.error(

src/agentready/assessors/documentation.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Documentation assessor for CLAUDE.md, README, docstrings, and ADRs."""
22

3-
from pathlib import Path
43

54
from ..models.attribute import Attribute
65
from ..models.finding import Citation, Finding, Remediation

src/agentready/assessors/repomix.py

Lines changed: 69 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""Repomix configuration assessor."""
22

33
from ..models.attribute import Attribute
4-
from ..models.finding import Finding
4+
from ..models.citation import Citation
5+
from ..models.finding import Finding, Remediation
56
from ..models.repository import Repository
67
from ..services.repomix import RepomixService
78
from .base import BaseAssessor
@@ -50,14 +51,9 @@ def assess(self, repository: Repository) -> Finding:
5051

5152
# Check if Repomix is configured
5253
if not service.has_config():
53-
return Finding.create_fail(
54-
self.attribute,
55-
score=0,
56-
evidence=[
57-
"Repomix configuration not found",
58-
"Missing repomix.config.json",
59-
],
60-
remediation_steps=[
54+
remediation = Remediation(
55+
summary="Initialize Repomix configuration",
56+
steps=[
6157
"Initialize Repomix: agentready repomix-generate --init",
6258
"Generate context: agentready repomix-generate",
6359
"Add to bootstrap: agentready bootstrap --repomix",
@@ -77,62 +73,101 @@ def assess(self, repository: Repository) -> Finding:
7773
"agentready repomix-generate --check"
7874
],
7975
citations=[
80-
{
81-
"title": "Repomix - AI-Friendly Repository Packager",
82-
"url": "https://github.com/yamadashy/repomix",
83-
"type": "tool",
84-
}
76+
Citation(
77+
source="Repomix GitHub Repository",
78+
title="Repomix - AI-Friendly Repository Packager",
79+
url="https://github.com/yamadashy/repomix",
80+
relevance="Tool for generating AI-optimized repository context",
81+
)
8582
],
8683
)
84+
return Finding(
85+
attribute=self.attribute,
86+
status="fail",
87+
score=0.0,
88+
measured_value=None,
89+
threshold="Configuration exists",
90+
evidence=[
91+
"Repomix configuration not found",
92+
"Missing repomix.config.json",
93+
],
94+
remediation=remediation,
95+
error_message=None,
96+
)
8797

8898
# Check if output exists and is fresh
8999
output_files = service.get_output_files()
90100
if not output_files:
91-
return Finding.create_fail(
92-
self.attribute,
93-
score=50,
94-
evidence=[
95-
"Repomix configuration exists",
96-
"No Repomix output files found",
97-
],
98-
remediation_steps=[
101+
remediation = Remediation(
102+
summary="Generate Repomix output",
103+
steps=[
99104
"Generate Repomix output: agentready repomix-generate",
100105
"Commit output if needed for team access",
101106
"Set up GitHub Action for automatic regeneration",
102107
],
103108
tools=["Repomix"],
104109
commands=["agentready repomix-generate"],
110+
examples=[],
111+
citations=[],
112+
)
113+
return Finding(
114+
attribute=self.attribute,
115+
status="fail",
116+
score=50.0,
117+
measured_value=None,
118+
threshold="Output files exist and are fresh",
119+
evidence=[
120+
"Repomix configuration exists",
121+
"No Repomix output files found",
122+
],
123+
remediation=remediation,
124+
error_message=None,
105125
)
106126

107127
# Check freshness (7 days max age)
108128
is_fresh, message = service.check_freshness(max_age_days=7)
109129

110130
if is_fresh:
111131
# All good - config exists, output exists and is fresh
112-
return Finding.create_pass(
113-
self.attribute,
114-
score=100,
132+
return Finding(
133+
attribute=self.attribute,
134+
status="pass",
135+
score=100.0,
136+
measured_value=message,
137+
threshold="< 7 days old",
115138
evidence=[
116139
"Repomix configuration exists",
117140
f"{len(output_files)} output file(s) found",
118141
message,
119142
],
143+
remediation=None,
144+
error_message=None,
120145
)
121146
else:
122147
# Config exists, output exists but is stale
123-
return Finding.create_fail(
124-
self.attribute,
125-
score=75,
126-
evidence=[
127-
"Repomix configuration exists",
128-
f"{len(output_files)} output file(s) found",
129-
message,
130-
],
131-
remediation_steps=[
148+
remediation = Remediation(
149+
summary="Regenerate Repomix output",
150+
steps=[
132151
"Regenerate Repomix output: agentready repomix-generate",
133152
"Set up GitHub Action for automatic weekly updates",
134153
"Add to pre-commit hooks for automatic regeneration",
135154
],
136155
tools=["Repomix"],
137156
commands=["agentready repomix-generate"],
157+
examples=[],
158+
citations=[],
159+
)
160+
return Finding(
161+
attribute=self.attribute,
162+
status="fail",
163+
score=75.0,
164+
measured_value=message,
165+
threshold="< 7 days old",
166+
evidence=[
167+
"Repomix configuration exists",
168+
f"{len(output_files)} output file(s) found",
169+
message,
170+
],
171+
remediation=remediation,
172+
error_message=None,
138173
)

src/agentready/assessors/structure.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"""Structure assessors for project layout and separation of concerns."""
22

3-
from pathlib import Path
43

54
from ..models.attribute import Attribute
65
from ..models.finding import Citation, Finding, Remediation

src/agentready/assessors/stub_assessors.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
enhanced later with more sophisticated detection and scoring logic.
55
"""
66

7-
from pathlib import Path
87

98
from ..models.attribute import Attribute
10-
from ..models.finding import Citation, Finding, Remediation
9+
from ..models.finding import Finding, Remediation
1110
from ..models.repository import Repository
1211
from .base import BaseAssessor
1312

src/agentready/assessors/testing.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
"""Testing assessors for test coverage, naming conventions, and pre-commit hooks."""
22

3-
import re
4-
import subprocess
5-
from pathlib import Path
63

74
from ..models.attribute import Attribute
85
from ..models.finding import Citation, Finding, Remediation

src/agentready/cli/bootstrap.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,12 @@
2121
default="auto",
2222
help="Primary language (default: auto-detect)",
2323
)
24-
def bootstrap(repository, dry_run, language):
24+
@click.option(
25+
"--repomix",
26+
is_flag=True,
27+
help="Initialize Repomix configuration for AI-friendly context generation",
28+
)
29+
def bootstrap(repository, dry_run, language, repomix):
2530
"""Bootstrap repository with GitHub infrastructure and best practices.
2631
2732
Creates:
@@ -30,6 +35,7 @@ def bootstrap(repository, dry_run, language):
3035
- Pre-commit hooks configuration
3136
- Dependabot configuration
3237
- Contributing guidelines
38+
- Repomix configuration (if --repomix flag is set)
3339
3440
REPOSITORY: Path to git repository (default: current directory)
3541
"""
@@ -44,14 +50,15 @@ def bootstrap(repository, dry_run, language):
4450
click.echo("=" * 50)
4551
click.echo(f"\nRepository: {repo_path}")
4652
click.echo(f"Language: {language}")
53+
click.echo(f"Repomix: {repomix}")
4754
click.echo(f"Dry run: {dry_run}\n")
4855

4956
# Create generator
5057
generator = BootstrapGenerator(repo_path, language)
5158

5259
# Generate all files
5360
try:
54-
created_files = generator.generate_all(dry_run=dry_run)
61+
created_files = generator.generate_all(dry_run=dry_run, enable_repomix=repomix)
5562
except Exception as e:
5663
click.echo(f"\nError during bootstrap: {str(e)}", err=True)
5764
sys.exit(1)

src/agentready/cli/demo.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -458,10 +458,14 @@ def demo(language, no_browser, keep_repo):
458458
click.echo()
459459

460460
# Display score with color based on level
461-
score_color = "green" if overall_score >= 75 else "yellow" if overall_score >= 60 else "red"
462-
click.echo(f" Overall Score: ", nl=False)
461+
score_color = (
462+
"green"
463+
if overall_score >= 75
464+
else "yellow" if overall_score >= 60 else "red"
465+
)
466+
click.echo(" Overall Score: ", nl=False)
463467
click.secho(f"{overall_score:.1f}/100", fg=score_color, bold=True)
464-
click.echo(f" Certification: ", nl=False)
468+
click.echo(" Certification: ", nl=False)
465469
click.secho(certification_level, fg=score_color, bold=True)
466470
click.echo(f" Assessed: {assessed}/25 attributes")
467471
click.echo(f" Skipped: {skipped} attributes")
@@ -537,7 +541,7 @@ def demo(language, no_browser, keep_repo):
537541
click.echo("Next steps:")
538542
click.echo(f" • View HTML report: {html_file}")
539543
click.echo(f" • View Markdown report: {md_file}")
540-
click.echo(f" • Assess your own repo: agentready assess /path/to/repo")
544+
click.echo(" • Assess your own repo: agentready assess /path/to/repo")
541545
click.echo()
542546

543547
if keep_repo:

src/agentready/cli/main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
# Import all assessors
2121
from ..assessors.documentation import CLAUDEmdAssessor, READMEAssessor
22+
from ..assessors.repomix import RepomixConfigAssessor
2223
from ..assessors.structure import StandardLayoutAssessor
2324
from ..assessors.stub_assessors import (
2425
ConventionalCommitsAssessor,
@@ -65,6 +66,8 @@ def create_all_assessors():
6566
ConventionalCommitsAssessor(),
6667
GitignoreAssessor(),
6768
CyclomaticComplexityAssessor(), # Actually Tier 3, but including here
69+
# Tier 3 Important (including Repomix)
70+
RepomixConfigAssessor(),
6871
]
6972

7073
# Add remaining stub assessors

src/agentready/services/bootstrap.py

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from jinja2 import Environment, PackageLoader, select_autoescape
77

88
from .language_detector import LanguageDetector
9+
from .repomix import RepomixService
910

1011

1112
class BootstrapGenerator:
@@ -42,19 +43,22 @@ def _detect_language(self, language: str) -> str:
4243
# Return most used language
4344
return max(languages, key=languages.get).lower()
4445

45-
def generate_all(self, dry_run: bool = False) -> List[Path]:
46+
def generate_all(
47+
self, dry_run: bool = False, enable_repomix: bool = False
48+
) -> List[Path]:
4649
"""Generate all bootstrap files.
4750
4851
Args:
4952
dry_run: If True, don't create files, just return paths
53+
enable_repomix: If True, generate Repomix configuration and workflow
5054
5155
Returns:
5256
List of created file paths
5357
"""
5458
created_files = []
5559

5660
# GitHub Actions workflows
57-
created_files.extend(self._generate_workflows(dry_run))
61+
created_files.extend(self._generate_workflows(dry_run, enable_repomix))
5862

5963
# GitHub templates
6064
created_files.extend(self._generate_github_templates(dry_run))
@@ -68,9 +72,15 @@ def generate_all(self, dry_run: bool = False) -> List[Path]:
6872
# Contributing guidelines
6973
created_files.extend(self._generate_docs(dry_run))
7074

75+
# Repomix configuration (if enabled)
76+
if enable_repomix:
77+
created_files.extend(self._generate_repomix_config(dry_run))
78+
7179
return created_files
7280

73-
def _generate_workflows(self, dry_run: bool) -> List[Path]:
81+
def _generate_workflows(
82+
self, dry_run: bool, enable_repomix: bool = False
83+
) -> List[Path]:
7484
"""Generate GitHub Actions workflows."""
7585
workflows_dir = self.repo_path / ".github" / "workflows"
7686
created = []
@@ -101,6 +111,13 @@ def _generate_workflows(self, dry_run: bool) -> List[Path]:
101111
content = template.render(language=test_language)
102112
created.append(self._write_file(security_workflow, content, dry_run))
103113

114+
# Repomix update workflow (if enabled)
115+
if enable_repomix:
116+
repomix_workflow = workflows_dir / "repomix-update.yml"
117+
template = self.env.get_template("workflows/repomix-update.yml.j2")
118+
content = template.render()
119+
created.append(self._write_file(repomix_workflow, content, dry_run))
120+
104121
return created
105122

106123
def _generate_github_templates(self, dry_run: bool) -> List[Path]:
@@ -177,6 +194,35 @@ def _generate_docs(self, dry_run: bool) -> List[Path]:
177194

178195
return created
179196

197+
def _generate_repomix_config(self, dry_run: bool) -> List[Path]:
198+
"""Generate Repomix configuration files.
199+
200+
Args:
201+
dry_run: If True, don't create files, just return paths
202+
203+
Returns:
204+
List of created file paths
205+
"""
206+
created = []
207+
208+
if not dry_run:
209+
# Use RepomixService to generate config and ignore files
210+
service = RepomixService(self.repo_path)
211+
212+
# Generate config (don't overwrite if exists)
213+
if service.generate_config(overwrite=False):
214+
created.append(service.config_path)
215+
216+
# Generate ignore file (don't overwrite if exists)
217+
if service.generate_ignore(overwrite=False):
218+
created.append(service.ignore_path)
219+
else:
220+
# For dry run, just return the paths that would be created
221+
created.append(self.repo_path / "repomix.config.json")
222+
created.append(self.repo_path / ".repomixignore")
223+
224+
return created
225+
180226
def _write_file(self, path: Path, content: str, dry_run: bool) -> Path:
181227
"""Write file to disk or simulate for dry run."""
182228
if dry_run:

0 commit comments

Comments
 (0)