From 1f2f3979d993900617c477aabf1bd68acaf7aa09 Mon Sep 17 00:00:00 2001
From: DataDave-Dev <153755137+DataDave-Dev@users.noreply.github.com>
Date: Tue, 30 Jun 2026 01:05:01 -0600
Subject: [PATCH] feat: add 'becwright list' to discover the built-in checks

Lists the built-in checks (discovered from the becwright.checks package) with a
short description each, plus the catalog URL. Closes #8.
---
 CLAUDE.md                        |  4 ++--
 README.es.md                     |  1 +
 README.md                        |  1 +
 documentation/architecture.es.md |  2 +-
 documentation/architecture.md    |  2 +-
 documentation/usage.es.md        |  1 +
 documentation/usage.md           |  1 +
 src/becwright/cli.py             | 31 +++++++++++++++++++++++++++++++
 tests/test_list.py               | 22 ++++++++++++++++++++++
 9 files changed, 61 insertions(+), 4 deletions(-)
 create mode 100644 tests/test_list.py

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.<name>){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}"
