Tool Auto-Discovery¶
Convention Over Configuration¶
codemol discovers tools automatically — no registration, no manifest file. Drop a Python file with a run() function into the right directory and it's available immediately.
How It Works¶
discover_tools() in tool_loader.py uses pkgutil.iter_modules():
import pkgutil
import importlib
from pathlib import Path
def discover_tools() -> dict[str, dict[str, object]]:
tools_dir = Path(__file__).parent.parent / "tools"
result = {}
for group_info in pkgutil.iter_modules([str(tools_dir)]):
if not group_info.ispkg:
continue
group_name = group_info.name
group_path = tools_dir / group_name
result[group_name] = {}
for tool_info in pkgutil.iter_modules([str(group_path)]):
if tool_info.ispkg:
continue
module = importlib.import_module(f"tools.{group_name}.{tool_info.name}")
if hasattr(module, "run"):
result[group_name][tool_info.name] = module
return result
Discovery Flow¶
graph TD
A[tools/] --> B{Scan directories}
B --> C[tools/io/]
B --> D[tools/measurements/]
B --> E[tools/representations/]
B --> F[...]
C --> G{Scan .py files}
G --> H[load.py]
G --> I[clear.py]
H --> J{Has run()?}
J -->|Yes| K["tools['io']['load'] = module"]
J -->|No| L[Skip]
The Contract¶
A valid tool module must have:
- A file in
tools/<group>/(not a subdirectory) - A
run()function as a module-level attribute
That's it. Everything else (docstrings, type hints, default values) is optional but recommended.
Argument Fitting: _fit_args()¶
Since the parser splits on spaces, multi-word selections get split into multiple args. _fit_args() uses inspect to examine the tool's run() signature and joins excess args:
import inspect
def _fit_args(func, args):
sig = inspect.signature(func)
params = [p for p in sig.parameters.values()
if p.name != "cmd"] # skip cmd parameter
# If tool accepts *args, pass through
if any(p.kind == p.VAR_POSITIONAL for p in params):
return args
# If more args than params, join extras into last param
max_positional = len(params)
if len(args) > max_positional and max_positional > 0:
fitted = args[:max_positional - 1]
fitted.append(" ".join(args[max_positional - 1:]))
return fitted
return args
Benefits of Auto-Discovery¶
| Benefit | Description |
|---|---|
| Zero boilerplate | No registration code needed |
| Instant availability | Drop file → command works |
| Natural grouping | Directory = tool group |
| Easy navigation | File name = command name |
| Testable | conftest.py discovers all tools for parametrized tests |