diff --git a/CLAUDE.md b/CLAUDE.md index 5c11207..7ddcf7e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,7 +33,7 @@ becwright/ ├── README.md # public conceptual document (English; README.es.md = Spanish) ├── pyproject.toml # packaging + `becwright` command (setuptools) ├── src/becwright/ # packaged ENGINE (installable, not copied into each repo) -│ ├── cli.py # argparse: init / check / install / uninstall / export / import +│ ├── cli.py # argparse: init / list / check / install / uninstall / export / import │ ├── engine.py # path matching + runs checks + decides pass/fail │ ├── rules.py # Rule model + loading of .bec/rules.yaml │ ├── bundle.py # export/import of BECs (the portable bundle) @@ -54,7 +54,7 @@ engine comes from the installed package. (Portability, C)** done: `becwright export` / `import` move a BEC between repos as a single self-contained `.bec.yaml` (a custom check's code travels embedded), with a trust gate that shows the code before installing. Commands: -`init / check / install / uninstall / export / import`. **Multi-language:** the engine +`init / list / check / install / uninstall / export / import`. **Multi-language:** the engine is agnostic (runs any check on any file); the generic `forbid` check (regex via `--pattern`) lets you write rules for any language without code, and the catalog includes Python and JS/TS BECs. Included checks: `forbid` (any language), diff --git a/README.es.md b/README.es.md index 681ee52..8ed0608 100644 --- a/README.es.md +++ b/README.es.md @@ -65,6 +65,7 @@ Comandos disponibles: | Comando | Qué hace | |---|---| | `becwright init` | Genera un `.bec/rules.yaml` de arranque e instala el hook | +| `becwright list` | Lista los checks incluidos | | `becwright check` | Corre las reglas sobre los archivos en staging | | `becwright install` | Instala el hook `pre-commit` nativo | | `becwright uninstall` | Quita el hook | diff --git a/README.md b/README.md index 23aab59..c73e8ec 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,7 @@ Available commands: | Command | What it does | |---|---| | `becwright init` | Scaffold a starter `.bec/rules.yaml` and install the hook | +| `becwright list` | List the built-in checks | | `becwright check` | Runs the rules over the staged files | | `becwright install` | Installs the native `pre-commit` hook | | `becwright uninstall` | Removes the hook | diff --git a/documentation/architecture.es.md b/documentation/architecture.es.md index 62c3eb3..8db76eb 100644 --- a/documentation/architecture.es.md +++ b/documentation/architecture.es.md @@ -10,7 +10,7 @@ filtra archivos por su ruta y corre un comando. | Módulo | Responsabilidad | |---|---| -| `cli.py` | CLI con argparse: `init / check / install / uninstall / export / import` | +| `cli.py` | CLI con argparse: `init / list / check / install / uninstall / export / import` | | `rules.py` | El modelo `Rule` y la carga de `.bec/rules.yaml` | | `engine.py` | Matching de rutas por glob, corre los checks, decide pasa/no-pasa | | `git.py` | Raíz del repo, archivos en staging, el hook pre-commit nativo | diff --git a/documentation/architecture.md b/documentation/architecture.md index 4510de2..d2da73b 100644 --- a/documentation/architecture.md +++ b/documentation/architecture.md @@ -10,7 +10,7 @@ itself — it matches files by path and runs a command. | Module | Responsibility | |---|---| -| `cli.py` | Argparse CLI: `init / check / install / uninstall / export / import` | +| `cli.py` | Argparse CLI: `init / list / check / install / uninstall / export / import` | | `rules.py` | The `Rule` model and loading of `.bec/rules.yaml` | | `engine.py` | Glob path matching, running checks, deciding pass/fail | | `git.py` | Repo root, staged files, the native pre-commit hook | diff --git a/documentation/usage.es.md b/documentation/usage.es.md index 0fe62e1..fbc95b4 100644 --- a/documentation/usage.es.md +++ b/documentation/usage.es.md @@ -28,6 +28,7 @@ a mano: `becwright install` más un `.bec/rules.yaml` que escribas vos.) | Comando | Descripción | |---|---| | `becwright init` | Genera un `.bec/rules.yaml` de arranque e instala el hook | +| `becwright list` | Lista los checks incluidos | | `becwright check` | Corre las reglas sobre los archivos en staging | | `becwright check --all` | Corre las reglas sobre todo el repo (`git ls-files`) | | `becwright install` | Instala el hook pre-commit | diff --git a/documentation/usage.md b/documentation/usage.md index e40830d..b18b9b8 100644 --- a/documentation/usage.md +++ b/documentation/usage.md @@ -27,6 +27,7 @@ From then on, every `git commit` runs the checks. (You can also set up by hand: | Command | Description | |---|---| | `becwright init` | Scaffold a starter `.bec/rules.yaml` and install the hook | +| `becwright list` | List the built-in checks | | `becwright check` | Run rules over the staged files | | `becwright check --all` | Run rules over the whole repo (`git ls-files`) | | `becwright install` | Install the pre-commit hook | diff --git a/src/becwright/cli.py b/src/becwright/cli.py index 127aa78..1d830e7 100644 --- a/src/becwright/cli.py +++ b/src/becwright/cli.py @@ -1,6 +1,7 @@ from __future__ import annotations import argparse +import pkgutil import sys import urllib.error import urllib.request @@ -70,6 +71,35 @@ def _cmd_uninstall(_: argparse.Namespace) -> int: return 0 +_CHECK_DESCRIPTIONS = { + "forbid": "fail if a regex (--pattern) appears in the files (any language)", + "no_token_in_logs": "tokens or credentials in log calls (Python)", + "hardcoded_secrets": "AWS keys, private keys, hardcoded password literals (any language)", + "debug_remnants": "leftover debugger / pdb statements (Python)", + "dangerous_eval": "eval / exec calls (any language)", + "wildcard_imports": "wildcard star imports (Python)", + "redundant_comments": "comments that restate the obvious code (Python, heuristic)", +} + + +def _builtin_check_names() -> list[str]: + from . import checks + return sorted(m.name for m in pkgutil.iter_modules(checks.__path__) if not m.name.startswith("_")) + + +def _cmd_list(_: argparse.Namespace) -> int: + print(f"{BOLD}Built-in checks{RESET} {DIM}(use as: python3 -m becwright.checks.){RESET}") + for name in _builtin_check_names(): + desc = _CHECK_DESCRIPTIONS.get(name, "") + line = f" {GREEN}{name}{RESET}" + if desc: + line += f" {DIM}{desc}{RESET}" + print(line) + print(f"\n{DIM}Catalog of ready-to-use BECs: " + f"https://github.com/DataDave-Dev/becwright/tree/main/becs{RESET}") + return 0 + + _SKIP_DIRS = {".git", "node_modules", ".venv", "venv", "__pycache__", "dist", "build", ".tox"} _EXT_LANG = {".py": "python", ".js": "js", ".ts": "ts"} @@ -248,6 +278,7 @@ def _build_parser() -> argparse.ArgumentParser: p_init.add_argument("--force", action="store_true", help="overwrite an existing .bec/rules.yaml") p_init.set_defaults(func=_cmd_init) + sub.add_parser("list", help="list the built-in checks").set_defaults(func=_cmd_list) sub.add_parser("install", help="install the pre-commit hook").set_defaults(func=_cmd_install) sub.add_parser("uninstall", help="remove the pre-commit hook").set_defaults(func=_cmd_uninstall) diff --git a/tests/test_list.py b/tests/test_list.py new file mode 100644 index 0000000..f3c337d --- /dev/null +++ b/tests/test_list.py @@ -0,0 +1,22 @@ +from becwright import cli + + +def test_builtin_check_names_includes_known_checks(): + names = cli._builtin_check_names() + assert "forbid" in names + assert "hardcoded_secrets" in names + assert "no_token_in_logs" in names + assert names == sorted(names) + + +def test_cmd_list_prints_checks_and_catalog(capsys): + assert cli.main(["list"]) == 0 + out = capsys.readouterr().out + assert "forbid" in out + assert "hardcoded_secrets" in out + assert "Catalog" in out + + +def test_every_listed_check_has_a_description(): + for name in cli._builtin_check_names(): + assert name in cli._CHECK_DESCRIPTIONS, f"missing description for {name}"