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.
Files changed (45) hide show
  1. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/PKG-INFO +48 -17
  2. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/README.md +47 -16
  3. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/pyproject.toml +1 -1
  4. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/__init__.py +1 -1
  5. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/__init__.py +43 -12
  6. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/cli.py +94 -9
  7. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/__init__.py +3 -0
  8. agentrepocoach-0.4.0/src/agentrepocoach/components/bootstrap_signals.py +204 -0
  9. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/compute.py +59 -1
  10. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/config.py +89 -10
  11. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/PKG-INFO +48 -17
  12. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/SOURCES.txt +4 -0
  13. agentrepocoach-0.4.0/tests/test_bootstrap_signals_security.py +33 -0
  14. agentrepocoach-0.4.0/tests/test_cli.py +108 -0
  15. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/tests/test_components.py +89 -1
  16. agentrepocoach-0.4.0/tests/test_config.py +197 -0
  17. agentrepocoach-0.4.0/tests/test_multi_language.py +197 -0
  18. agentrepocoach-0.3.0/tests/test_config.py +0 -101
  19. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/LICENSE +0 -0
  20. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/setup.cfg +0 -0
  21. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/__main__.py +0 -0
  22. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/base.py +0 -0
  23. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/csharp.py +0 -0
  24. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/go.py +0 -0
  25. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/python.py +0 -0
  26. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/rust.py +0 -0
  27. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/adapters/typescript.py +0 -0
  28. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/decision_queryability.py +0 -0
  29. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/documentation.py +0 -0
  30. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/error_quality.py +0 -0
  31. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/module_hygiene.py +0 -0
  32. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/components/test_quality.py +0 -0
  33. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/output.py +0 -0
  34. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/pr_bot.py +0 -0
  35. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/regex_safety.py +0 -0
  36. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach/scoring.py +0 -0
  37. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/dependency_links.txt +0 -0
  38. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/entry_points.txt +0 -0
  39. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/requires.txt +0 -0
  40. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/src/agentrepocoach.egg-info/top_level.txt +0 -0
  41. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/tests/test_adapters.py +0 -0
  42. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/tests/test_cli_compare.py +0 -0
  43. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/tests/test_output.py +0 -0
  44. {agentrepocoach-0.3.0 → agentrepocoach-0.4.0}/tests/test_pr_bot.py +0 -0
  45. {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.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 five statically-measurable components:
46
+ It blends six statically-measurable components:
47
47
 
48
- - **Navigability** (25%) — `AGENTS.md`, codebase map, CLI manifest, root cleanliness
49
- - **Error quality** (25%) — fix-hint coverage, exception typing, generic-exception dominance
50
- - **Decision queryability** (20%) — ADR catalog, inline reference resolution
51
- - **Test quality** (15%) — naming convention, helper presence, fixture duplication
52
- - **Module hygiene** (15%) — internal visibility, god files, doc coverage, architecture doc freshness
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.25
148
- error_quality = 0.25
149
- decision_queryability = 0.20
150
- test_quality = 0.15
151
- module_hygiene = 0.15
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 five component scorers against the adapter's view of
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 five statically-measurable components:
12
+ It blends six statically-measurable components:
13
13
 
14
- - **Navigability** (25%) — `AGENTS.md`, codebase map, CLI manifest, root cleanliness
15
- - **Error quality** (25%) — fix-hint coverage, exception typing, generic-exception dominance
16
- - **Decision queryability** (20%) — ADR catalog, inline reference resolution
17
- - **Test quality** (15%) — naming convention, helper presence, fixture duplication
18
- - **Module hygiene** (15%) — internal visibility, god files, doc coverage, architecture doc freshness
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.25
114
- error_quality = 0.25
115
- decision_queryability = 0.20
116
- test_quality = 0.15
117
- module_hygiene = 0.15
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 five component scorers against the adapter's view of
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.3.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" }
@@ -9,6 +9,6 @@ from __future__ import annotations
9
9
 
10
10
  from .compute import compute_cah
11
11
 
12
- VERSION = "0.3.0"
12
+ VERSION = "0.4.0"
13
13
 
14
14
  __all__ = ["compute_cah", "VERSION"]
@@ -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: list[tuple[float, LanguageAdapter]] = []
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=Path.cwd(),
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
- parser.add_argument(
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 (csharp|python|auto).",
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
- args = parser.parse_args(argv)
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
- repo_root = args.repo.resolve()
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",