agentrepocoach 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/PKG-INFO +48 -17
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/README.md +47 -16
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/pyproject.toml +1 -1
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/__init__.py +1 -1
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/__init__.py +43 -12
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/cli.py +94 -9
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/__init__.py +3 -0
- agentrepocoach-0.4.0/src/agentrepocoach/components/bootstrap_signals.py +204 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/compute.py +59 -1
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/config.py +89 -10
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/PKG-INFO +48 -17
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/SOURCES.txt +4 -0
- agentrepocoach-0.4.0/tests/test_bootstrap_signals_security.py +33 -0
- agentrepocoach-0.4.0/tests/test_cli.py +108 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/tests/test_components.py +89 -1
- agentrepocoach-0.4.0/tests/test_config.py +197 -0
- agentrepocoach-0.4.0/tests/test_multi_language.py +197 -0
- agentrepocoach-0.3.0/tests/test_config.py +0 -101
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/LICENSE +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/setup.cfg +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/__main__.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/base.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/csharp.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/go.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/python.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/rust.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/typescript.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/decision_queryability.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/documentation.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/error_quality.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/module_hygiene.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/test_quality.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/output.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/pr_bot.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/regex_safety.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/scoring.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/dependency_links.txt +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/entry_points.txt +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/requires.txt +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/top_level.txt +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/tests/test_adapters.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/tests/test_cli_compare.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/tests/test_output.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/tests/test_pr_bot.py +0 -0
- {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/tests/test_regex_safety.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentrepocoach
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Score your codebase on how ready it is for AI agents — and coach you through the fixes.
|
|
5
5
|
Author: WouterDeBot
|
|
6
6
|
License: Apache-2.0
|
|
@@ -43,13 +43,14 @@ Dynamic: license-file
|
|
|
43
43
|
|
|
44
44
|
AgentRepoCoach computes the **Codebase Agent Health (CAH)** score: a single 0-100
|
|
45
45
|
composite measuring how friendly a repository is for autonomous AI agents.
|
|
46
|
-
It blends
|
|
46
|
+
It blends six statically-measurable components:
|
|
47
47
|
|
|
48
|
-
- **Navigability** (
|
|
49
|
-
- **Error quality** (
|
|
50
|
-
- **Decision queryability** (
|
|
51
|
-
- **Test quality** (
|
|
52
|
-
- **Module hygiene** (
|
|
48
|
+
- **Navigability** (22%) — `AGENTS.md`, codebase map, CLI manifest, root cleanliness
|
|
49
|
+
- **Error quality** (22%) — fix-hint coverage, exception typing, generic-exception dominance
|
|
50
|
+
- **Decision queryability** (18%) — ADR catalog, inline reference resolution
|
|
51
|
+
- **Test quality** (13%) — naming convention, helper presence, fixture duplication
|
|
52
|
+
- **Module hygiene** (13%) — internal visibility, god files, doc coverage, architecture doc freshness
|
|
53
|
+
- **Bootstrap signals** (12%) — CI-Signal (runnable test workflow on PR triggers) + README-quality (install + test commands in first 100 lines)
|
|
53
54
|
|
|
54
55
|
AgentRepoCoach ships with zero runtime dependencies — it uses the Python 3.11+
|
|
55
56
|
standard library only, including `tomllib` for config parsing.
|
|
@@ -111,9 +112,21 @@ jobs:
|
|
|
111
112
|
|
|
112
113
|
## Usage as a CLI
|
|
113
114
|
|
|
115
|
+
Install and run:
|
|
116
|
+
|
|
114
117
|
```bash
|
|
115
118
|
pip install agentrepocoach
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Run tests after contributing:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pytest tests/ -q
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Score your repository:
|
|
116
128
|
|
|
129
|
+
```bash
|
|
117
130
|
# Score the current directory (prints a summary table)
|
|
118
131
|
python -m agentrepocoach.cli --repo .
|
|
119
132
|
|
|
@@ -139,16 +152,19 @@ AgentRepoCoach looks for `.agentrepocoach.toml` at the repo root. Every field is
|
|
|
139
152
|
optional — the tool ships with sensible defaults and will score zero-config
|
|
140
153
|
repos without complaint.
|
|
141
154
|
|
|
142
|
-
Minimal example:
|
|
155
|
+
Minimal example (schema v2, required since v0.4.0):
|
|
143
156
|
|
|
144
157
|
```toml
|
|
145
158
|
# .agentrepocoach.toml
|
|
159
|
+
schema_version = 2
|
|
160
|
+
|
|
146
161
|
[weights]
|
|
147
|
-
navigability = 0.
|
|
148
|
-
error_quality = 0.
|
|
149
|
-
decision_queryability = 0.
|
|
150
|
-
test_quality = 0.
|
|
151
|
-
module_hygiene = 0.
|
|
162
|
+
navigability = 0.22
|
|
163
|
+
error_quality = 0.22
|
|
164
|
+
decision_queryability = 0.18
|
|
165
|
+
test_quality = 0.13
|
|
166
|
+
module_hygiene = 0.13
|
|
167
|
+
bootstrap_signals = 0.12
|
|
152
168
|
|
|
153
169
|
[paths]
|
|
154
170
|
adr_dir = "docs/adr/"
|
|
@@ -159,8 +175,17 @@ domain_exception_types = ["DomainError", "ValidationError"]
|
|
|
159
175
|
|
|
160
176
|
[decision_queryability]
|
|
161
177
|
inline_ref_patterns = ["ADR-\\d+"]
|
|
178
|
+
|
|
179
|
+
# Optional: tune bootstrap_signals detection globs / patterns
|
|
180
|
+
[bootstrap_signals]
|
|
181
|
+
ci_workflow_globs = [".github/workflows/*.yml", ".gitlab-ci.yml"]
|
|
162
182
|
```
|
|
163
183
|
|
|
184
|
+
**Migrating from v1?** Add `schema_version = 2` at the top and add
|
|
185
|
+
`bootstrap_signals = 0.12` to `[weights]`, then rebalance the other five
|
|
186
|
+
weights so they still sum to 1.0. See [`docs/configuration.md`](docs/configuration.md)
|
|
187
|
+
for the one-line migration recipe.
|
|
188
|
+
|
|
164
189
|
See [`docs/METHODOLOGY.md`](docs/METHODOLOGY.md) for the full config schema
|
|
165
190
|
and scoring formula.
|
|
166
191
|
|
|
@@ -192,10 +217,19 @@ appear in:
|
|
|
192
217
|
## How it works
|
|
193
218
|
|
|
194
219
|
AgentRepoCoach detects the primary language of the repo, loads a language
|
|
195
|
-
adapter, and runs
|
|
220
|
+
adapter, and runs six component scorers against the adapter's view of
|
|
196
221
|
the codebase. Each component returns a 0-100 sub-score with a transparent
|
|
197
222
|
breakdown. The weighted sum is the composite CAH score.
|
|
198
223
|
|
|
224
|
+
The six components are:
|
|
225
|
+
|
|
226
|
+
- **Navigability** — checks for `AGENTS.md`, codebase map, CLI manifest, and root cleanliness.
|
|
227
|
+
- **Error quality** — scores fix-hint coverage, exception typing, and generic-exception usage.
|
|
228
|
+
- **Decision queryability** — audits ADR catalog completeness and inline ADR cross-references.
|
|
229
|
+
- **Test quality** — checks test naming conventions, helper presence, and fixture duplication.
|
|
230
|
+
- **Module hygiene** — inspects internal visibility ratios, god files, doc coverage, and architecture doc freshness.
|
|
231
|
+
- **Bootstrap signals** — language-agnostic CI-Signal scorer (does the repo have a CI workflow triggered on pull requests?) and README-quality scorer (do the first 100 README lines contain both an install and a test command in fenced code blocks?). Both are configurable via `[bootstrap_signals]` in `.agentrepocoach.toml`.
|
|
232
|
+
|
|
199
233
|
Every output field is a count, percentage, type name, or file path —
|
|
200
234
|
AgentRepoCoach never emits code snippets or raw message bodies, so reports
|
|
201
235
|
are safe to publish as CI artifacts.
|
|
@@ -203,6 +237,3 @@ are safe to publish as CI artifacts.
|
|
|
203
237
|
## License
|
|
204
238
|
|
|
205
239
|
Apache 2.0. See [LICENSE](LICENSE).
|
|
206
|
-
|
|
207
|
-
Built using the [GSD](https://github.com/gsd-build/get-shit-done) workflow
|
|
208
|
-
methodology (MIT).
|
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
|
|
10
10
|
AgentRepoCoach computes the **Codebase Agent Health (CAH)** score: a single 0-100
|
|
11
11
|
composite measuring how friendly a repository is for autonomous AI agents.
|
|
12
|
-
It blends
|
|
12
|
+
It blends six statically-measurable components:
|
|
13
13
|
|
|
14
|
-
- **Navigability** (
|
|
15
|
-
- **Error quality** (
|
|
16
|
-
- **Decision queryability** (
|
|
17
|
-
- **Test quality** (
|
|
18
|
-
- **Module hygiene** (
|
|
14
|
+
- **Navigability** (22%) — `AGENTS.md`, codebase map, CLI manifest, root cleanliness
|
|
15
|
+
- **Error quality** (22%) — fix-hint coverage, exception typing, generic-exception dominance
|
|
16
|
+
- **Decision queryability** (18%) — ADR catalog, inline reference resolution
|
|
17
|
+
- **Test quality** (13%) — naming convention, helper presence, fixture duplication
|
|
18
|
+
- **Module hygiene** (13%) — internal visibility, god files, doc coverage, architecture doc freshness
|
|
19
|
+
- **Bootstrap signals** (12%) — CI-Signal (runnable test workflow on PR triggers) + README-quality (install + test commands in first 100 lines)
|
|
19
20
|
|
|
20
21
|
AgentRepoCoach ships with zero runtime dependencies — it uses the Python 3.11+
|
|
21
22
|
standard library only, including `tomllib` for config parsing.
|
|
@@ -77,9 +78,21 @@ jobs:
|
|
|
77
78
|
|
|
78
79
|
## Usage as a CLI
|
|
79
80
|
|
|
81
|
+
Install and run:
|
|
82
|
+
|
|
80
83
|
```bash
|
|
81
84
|
pip install agentrepocoach
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Run tests after contributing:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pytest tests/ -q
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Score your repository:
|
|
82
94
|
|
|
95
|
+
```bash
|
|
83
96
|
# Score the current directory (prints a summary table)
|
|
84
97
|
python -m agentrepocoach.cli --repo .
|
|
85
98
|
|
|
@@ -105,16 +118,19 @@ AgentRepoCoach looks for `.agentrepocoach.toml` at the repo root. Every field is
|
|
|
105
118
|
optional — the tool ships with sensible defaults and will score zero-config
|
|
106
119
|
repos without complaint.
|
|
107
120
|
|
|
108
|
-
Minimal example:
|
|
121
|
+
Minimal example (schema v2, required since v0.4.0):
|
|
109
122
|
|
|
110
123
|
```toml
|
|
111
124
|
# .agentrepocoach.toml
|
|
125
|
+
schema_version = 2
|
|
126
|
+
|
|
112
127
|
[weights]
|
|
113
|
-
navigability = 0.
|
|
114
|
-
error_quality = 0.
|
|
115
|
-
decision_queryability = 0.
|
|
116
|
-
test_quality = 0.
|
|
117
|
-
module_hygiene = 0.
|
|
128
|
+
navigability = 0.22
|
|
129
|
+
error_quality = 0.22
|
|
130
|
+
decision_queryability = 0.18
|
|
131
|
+
test_quality = 0.13
|
|
132
|
+
module_hygiene = 0.13
|
|
133
|
+
bootstrap_signals = 0.12
|
|
118
134
|
|
|
119
135
|
[paths]
|
|
120
136
|
adr_dir = "docs/adr/"
|
|
@@ -125,8 +141,17 @@ domain_exception_types = ["DomainError", "ValidationError"]
|
|
|
125
141
|
|
|
126
142
|
[decision_queryability]
|
|
127
143
|
inline_ref_patterns = ["ADR-\\d+"]
|
|
144
|
+
|
|
145
|
+
# Optional: tune bootstrap_signals detection globs / patterns
|
|
146
|
+
[bootstrap_signals]
|
|
147
|
+
ci_workflow_globs = [".github/workflows/*.yml", ".gitlab-ci.yml"]
|
|
128
148
|
```
|
|
129
149
|
|
|
150
|
+
**Migrating from v1?** Add `schema_version = 2` at the top and add
|
|
151
|
+
`bootstrap_signals = 0.12` to `[weights]`, then rebalance the other five
|
|
152
|
+
weights so they still sum to 1.0. See [`docs/configuration.md`](docs/configuration.md)
|
|
153
|
+
for the one-line migration recipe.
|
|
154
|
+
|
|
130
155
|
See [`docs/METHODOLOGY.md`](docs/METHODOLOGY.md) for the full config schema
|
|
131
156
|
and scoring formula.
|
|
132
157
|
|
|
@@ -158,10 +183,19 @@ appear in:
|
|
|
158
183
|
## How it works
|
|
159
184
|
|
|
160
185
|
AgentRepoCoach detects the primary language of the repo, loads a language
|
|
161
|
-
adapter, and runs
|
|
186
|
+
adapter, and runs six component scorers against the adapter's view of
|
|
162
187
|
the codebase. Each component returns a 0-100 sub-score with a transparent
|
|
163
188
|
breakdown. The weighted sum is the composite CAH score.
|
|
164
189
|
|
|
190
|
+
The six components are:
|
|
191
|
+
|
|
192
|
+
- **Navigability** — checks for `AGENTS.md`, codebase map, CLI manifest, and root cleanliness.
|
|
193
|
+
- **Error quality** — scores fix-hint coverage, exception typing, and generic-exception usage.
|
|
194
|
+
- **Decision queryability** — audits ADR catalog completeness and inline ADR cross-references.
|
|
195
|
+
- **Test quality** — checks test naming conventions, helper presence, and fixture duplication.
|
|
196
|
+
- **Module hygiene** — inspects internal visibility ratios, god files, doc coverage, and architecture doc freshness.
|
|
197
|
+
- **Bootstrap signals** — language-agnostic CI-Signal scorer (does the repo have a CI workflow triggered on pull requests?) and README-quality scorer (do the first 100 README lines contain both an install and a test command in fenced code blocks?). Both are configurable via `[bootstrap_signals]` in `.agentrepocoach.toml`.
|
|
198
|
+
|
|
165
199
|
Every output field is a count, percentage, type name, or file path —
|
|
166
200
|
AgentRepoCoach never emits code snippets or raw message bodies, so reports
|
|
167
201
|
are safe to publish as CI artifacts.
|
|
@@ -169,6 +203,3 @@ are safe to publish as CI artifacts.
|
|
|
169
203
|
## License
|
|
170
204
|
|
|
171
205
|
Apache 2.0. See [LICENSE](LICENSE).
|
|
172
|
-
|
|
173
|
-
Built using the [GSD](https://github.com/gsd-build/get-shit-done) workflow
|
|
174
|
-
methodology (MIT).
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentrepocoach"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.4.0"
|
|
8
8
|
description = "Score your codebase on how ready it is for AI agents — and coach you through the fixes."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "Apache-2.0" }
|
|
@@ -32,6 +32,21 @@ def get_adapter_by_name(name: str) -> LanguageAdapter:
|
|
|
32
32
|
return _REGISTRY[name]()
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
def _collect_candidates(repo_path: Path) -> list[tuple[float, LanguageAdapter]]:
|
|
36
|
+
"""Collect all adapters with confidence > 0.0, sorted descending by (confidence, file_count)."""
|
|
37
|
+
candidates: list[tuple[float, LanguageAdapter]] = []
|
|
38
|
+
for cls in _REGISTRY.values():
|
|
39
|
+
adapter = cls()
|
|
40
|
+
confidence = adapter.detect(repo_path)
|
|
41
|
+
if confidence > 0.0:
|
|
42
|
+
candidates.append((confidence, adapter))
|
|
43
|
+
candidates.sort(
|
|
44
|
+
key=lambda pair: (pair[0], len(pair[1].find_production_files(repo_path))),
|
|
45
|
+
reverse=True,
|
|
46
|
+
)
|
|
47
|
+
return candidates
|
|
48
|
+
|
|
49
|
+
|
|
35
50
|
def detect_primary(repo_path: Path) -> LanguageAdapter:
|
|
36
51
|
"""Try every adapter and return the one with the highest detect() confidence.
|
|
37
52
|
|
|
@@ -39,25 +54,40 @@ def detect_primary(repo_path: Path) -> LanguageAdapter:
|
|
|
39
54
|
``find_production_files`` returns more files wins — a repo with 20 .py
|
|
40
55
|
files and a single .sln fixture is almost certainly a Python project.
|
|
41
56
|
"""
|
|
42
|
-
candidates
|
|
43
|
-
for cls in _REGISTRY.values():
|
|
44
|
-
adapter = cls()
|
|
45
|
-
confidence = adapter.detect(repo_path)
|
|
46
|
-
if confidence > 0.0:
|
|
47
|
-
candidates.append((confidence, adapter))
|
|
57
|
+
candidates = _collect_candidates(repo_path)
|
|
48
58
|
if not candidates:
|
|
49
59
|
supported = ", ".join(sorted(_REGISTRY))
|
|
50
60
|
msg = f"No supported language detected in {repo_path}. Supported: {supported}."
|
|
51
61
|
raise NoAdapterError(f"{msg} Try using --language to force an adapter, or check that the repo contains a recognized project file.")
|
|
52
|
-
# Sort by confidence (desc), then by production file count (desc) to
|
|
53
|
-
# break ties deterministically in favour of the dominant language.
|
|
54
|
-
candidates.sort(
|
|
55
|
-
key=lambda pair: (pair[0], len(pair[1].find_production_files(repo_path))),
|
|
56
|
-
reverse=True,
|
|
57
|
-
)
|
|
58
62
|
return candidates[0][1]
|
|
59
63
|
|
|
60
64
|
|
|
65
|
+
def detect_all(repo_path: Path) -> list[tuple[float, LanguageAdapter]]:
|
|
66
|
+
"""Return all adapters that meet the detection threshold.
|
|
67
|
+
|
|
68
|
+
An adapter is included when ``confidence >= 0.5`` AND it has
|
|
69
|
+
``file_count >= 3`` production files under ``repo_path``. This filters
|
|
70
|
+
out repos where a language appears only as tooling sprinkles (e.g. a
|
|
71
|
+
single Makefile helper script in an otherwise Go repo).
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
A list of ``(confidence, adapter)`` tuples, sorted descending by
|
|
75
|
+
``(confidence, file_count)``. Returns an empty list when no adapter
|
|
76
|
+
meets the threshold.
|
|
77
|
+
"""
|
|
78
|
+
_CONFIDENCE_FLOOR = 0.5
|
|
79
|
+
_FILE_COUNT_FLOOR = 3
|
|
80
|
+
|
|
81
|
+
result: list[tuple[float, LanguageAdapter]] = []
|
|
82
|
+
for confidence, adapter in _collect_candidates(repo_path):
|
|
83
|
+
if confidence < _CONFIDENCE_FLOOR:
|
|
84
|
+
continue
|
|
85
|
+
if len(adapter.find_production_files(repo_path)) < _FILE_COUNT_FLOOR:
|
|
86
|
+
continue
|
|
87
|
+
result.append((confidence, adapter))
|
|
88
|
+
return result
|
|
89
|
+
|
|
90
|
+
|
|
61
91
|
__all__ = [
|
|
62
92
|
"CSharpAdapter",
|
|
63
93
|
"Declaration",
|
|
@@ -69,6 +99,7 @@ __all__ = [
|
|
|
69
99
|
"RustAdapter",
|
|
70
100
|
"ThrowSite",
|
|
71
101
|
"TypeScriptAdapter",
|
|
102
|
+
"detect_all",
|
|
72
103
|
"detect_primary",
|
|
73
104
|
"get_adapter_by_name",
|
|
74
105
|
]
|
|
@@ -7,9 +7,9 @@ import sys
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
9
|
from . import VERSION
|
|
10
|
-
from .adapters import NoAdapterError
|
|
11
|
-
from .compute import compute_cah
|
|
12
|
-
from .config import ConfigError, load_config
|
|
10
|
+
from .adapters import NoAdapterError, _REGISTRY
|
|
11
|
+
from .compute import compute_cah, compute_cah_all
|
|
12
|
+
from .config import Config, ConfigError, load_config
|
|
13
13
|
from .output import (
|
|
14
14
|
format_comparison,
|
|
15
15
|
format_comparison_markdown,
|
|
@@ -33,8 +33,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
33
33
|
parser.add_argument(
|
|
34
34
|
"--repo",
|
|
35
35
|
type=Path,
|
|
36
|
-
default=
|
|
37
|
-
help="Path to the repository to score (default: current directory)."
|
|
36
|
+
default=None,
|
|
37
|
+
help="Path to the repository to score (default: current directory). "
|
|
38
|
+
"You may also pass the path as a positional argument: agentrepocoach [PATH].",
|
|
38
39
|
)
|
|
39
40
|
parser.add_argument(
|
|
40
41
|
"--config",
|
|
@@ -42,11 +43,20 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
42
43
|
default=None,
|
|
43
44
|
help="Explicit config file path (default: <repo>/.agentrepocoach.toml).",
|
|
44
45
|
)
|
|
45
|
-
|
|
46
|
+
_adapter_names = "|".join(sorted(_REGISTRY)) + "|auto"
|
|
47
|
+
lang_group = parser.add_mutually_exclusive_group()
|
|
48
|
+
lang_group.add_argument(
|
|
46
49
|
"--language",
|
|
47
50
|
type=str,
|
|
48
51
|
default=None,
|
|
49
|
-
help="Override language detection (
|
|
52
|
+
help=f"Override language detection ({_adapter_names}). Mutually exclusive with --all-languages.",
|
|
53
|
+
)
|
|
54
|
+
lang_group.add_argument(
|
|
55
|
+
"--all-languages",
|
|
56
|
+
action="store_true",
|
|
57
|
+
default=False,
|
|
58
|
+
dest="all_languages",
|
|
59
|
+
help="Score every detected language above threshold. Mutually exclusive with --language.",
|
|
50
60
|
)
|
|
51
61
|
parser.add_argument("--json", type=Path, help="Write full JSON result to this path.")
|
|
52
62
|
parser.add_argument("--prometheus", type=Path, help="Write Prometheus metrics to this path.")
|
|
@@ -142,13 +152,40 @@ def _run_compare(args: argparse.Namespace) -> int:
|
|
|
142
152
|
def main(argv: list[str] | None = None) -> int:
|
|
143
153
|
"""Run the CLI, parse arguments, compute the CAH score, and write outputs."""
|
|
144
154
|
parser = build_parser()
|
|
145
|
-
|
|
155
|
+
|
|
156
|
+
# Pre-process argv to support positional path: `agentrepocoach .`
|
|
157
|
+
# If the first non-option argument is not a known subcommand and looks like
|
|
158
|
+
# a path (not starting with '-'), inject it as `--repo` so argparse handles
|
|
159
|
+
# the rest normally. This preserves full backward-compat with `--repo` and
|
|
160
|
+
# with subcommands like `compare`.
|
|
161
|
+
_argv = list(argv) if argv is not None else sys.argv[1:]
|
|
162
|
+
_known_subcommands = {"compare"}
|
|
163
|
+
_positional_path: Path | None = None
|
|
164
|
+
if _argv and not _argv[0].startswith("-") and _argv[0] not in _known_subcommands:
|
|
165
|
+
_positional_path = Path(_argv[0])
|
|
166
|
+
_argv = _argv[1:]
|
|
167
|
+
|
|
168
|
+
args = parser.parse_args(_argv)
|
|
146
169
|
|
|
147
170
|
# Dispatch subcommands
|
|
148
171
|
if args.subcommand == "compare":
|
|
149
172
|
return _run_compare(args)
|
|
150
173
|
|
|
151
|
-
|
|
174
|
+
# Resolve repo path: --repo wins over positional; default is cwd.
|
|
175
|
+
if args.repo is not None and _positional_path is not None:
|
|
176
|
+
print(
|
|
177
|
+
"notice: both --repo and positional PATH given; --repo takes precedence.",
|
|
178
|
+
file=sys.stderr,
|
|
179
|
+
)
|
|
180
|
+
resolved_repo = args.repo
|
|
181
|
+
elif args.repo is not None:
|
|
182
|
+
resolved_repo = args.repo
|
|
183
|
+
elif _positional_path is not None:
|
|
184
|
+
resolved_repo = _positional_path
|
|
185
|
+
else:
|
|
186
|
+
resolved_repo = Path.cwd()
|
|
187
|
+
|
|
188
|
+
repo_root = resolved_repo.resolve()
|
|
152
189
|
if not repo_root.is_dir():
|
|
153
190
|
print(f"error: repo path is not a directory: {repo_root}", file=sys.stderr)
|
|
154
191
|
return 2
|
|
@@ -164,6 +201,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
164
201
|
from dataclasses import replace as _replace
|
|
165
202
|
config = _replace(config, language=args.language)
|
|
166
203
|
|
|
204
|
+
if args.all_languages:
|
|
205
|
+
return _run_all_languages(repo_root, config, args)
|
|
206
|
+
|
|
167
207
|
try:
|
|
168
208
|
result = compute_cah(repo_root, config=config)
|
|
169
209
|
except NoAdapterError as exc:
|
|
@@ -221,6 +261,51 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
221
261
|
return 0
|
|
222
262
|
|
|
223
263
|
|
|
264
|
+
def _run_all_languages(
|
|
265
|
+
repo_root: Path,
|
|
266
|
+
config: Config,
|
|
267
|
+
args: argparse.Namespace,
|
|
268
|
+
) -> int:
|
|
269
|
+
"""Handle the --all-languages code path."""
|
|
270
|
+
try:
|
|
271
|
+
result = compute_cah_all(repo_root, config=config)
|
|
272
|
+
except (NoAdapterError, RuntimeError) as exc:
|
|
273
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
274
|
+
return 2
|
|
275
|
+
|
|
276
|
+
languages: dict = result.get("languages", {})
|
|
277
|
+
|
|
278
|
+
if not languages:
|
|
279
|
+
print("No language met the detection threshold (confidence >= 0.5, file count >= 3).", file=sys.stderr)
|
|
280
|
+
return 2
|
|
281
|
+
|
|
282
|
+
# Text output: one summary block per language, separated by a header line.
|
|
283
|
+
if not args.quiet:
|
|
284
|
+
first = True
|
|
285
|
+
for lang_name, lang_result in languages.items():
|
|
286
|
+
if not first:
|
|
287
|
+
print()
|
|
288
|
+
print(f"=== Language: {lang_name} ===")
|
|
289
|
+
print(format_summary(lang_result))
|
|
290
|
+
first = False
|
|
291
|
+
|
|
292
|
+
# JSON file output: write the nested multi-language shape.
|
|
293
|
+
if args.json:
|
|
294
|
+
write_json(result, args.json)
|
|
295
|
+
if not args.quiet:
|
|
296
|
+
print(f"\nJSON report written to {args.json}")
|
|
297
|
+
|
|
298
|
+
# --format json to stdout.
|
|
299
|
+
if args.format == "json" and not args.output:
|
|
300
|
+
print(render_json(result))
|
|
301
|
+
elif args.format == "json" and args.output:
|
|
302
|
+
write_json(result, args.output)
|
|
303
|
+
if not args.quiet:
|
|
304
|
+
print(f"\nJSON report written to {args.output}")
|
|
305
|
+
|
|
306
|
+
return 0
|
|
307
|
+
|
|
308
|
+
|
|
224
309
|
def _print_formatted(result: dict, fmt: str) -> None:
|
|
225
310
|
"""Print formatted output to stdout when --output is not provided."""
|
|
226
311
|
if fmt == "json":
|
|
@@ -11,7 +11,9 @@ File-to-component mapping:
|
|
|
11
11
|
- ``decision_queryability.py`` -> ``decision_queryability``
|
|
12
12
|
- ``test_quality.py`` -> ``test_quality``
|
|
13
13
|
- ``module_hygiene.py`` -> ``module_hygiene``
|
|
14
|
+
- ``bootstrap_signals.py`` -> ``bootstrap_signals`` (CI workflow + README quality)
|
|
14
15
|
"""
|
|
16
|
+
from .bootstrap_signals import compute_bootstrap_signals
|
|
15
17
|
from .decision_queryability import compute_decision_queryability
|
|
16
18
|
from .documentation import compute_navigability
|
|
17
19
|
from .error_quality import compute_error_quality
|
|
@@ -19,6 +21,7 @@ from .module_hygiene import compute_module_hygiene
|
|
|
19
21
|
from .test_quality import compute_test_quality
|
|
20
22
|
|
|
21
23
|
__all__ = [
|
|
24
|
+
"compute_bootstrap_signals",
|
|
22
25
|
"compute_decision_queryability",
|
|
23
26
|
"compute_error_quality",
|
|
24
27
|
"compute_module_hygiene",
|