Quality checks run locally via prek pre-push hooks. CI workflows (ruff, ty, pytest, lint-infra, version-bump) are no longer needed. Updated docs to remove all GitHub Actions references.
6.9 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
What This Is
hate_crack is a menu-driven Python wrapper for hashcat that automates password cracking methodologies. It provides 16 attack modes, API integrations (Hashview, Weakpass, Hashmob), and utilities for wordlist/rule management.
Commands
# Install (builds submodules, vendors assets, installs via uv)
make install
# Dev install (editable, with dev deps)
make dev-install
# Run tests (requires HATE_CRACK_SKIP_INIT=1 in worktrees without hashcat-utils)
HATE_CRACK_SKIP_INIT=1 uv run pytest -v
# Run a single test file
HATE_CRACK_SKIP_INIT=1 uv run pytest tests/test_ui_menu_options.py -v
# Lint
uv run ruff check hate_crack
uv run ty check hate_crack
# Both lint checks
make lint
# Format
uv run ruff format hate_crack
# Coverage
make coverage
Test environment variables: HATE_CRACK_SKIP_INIT=1 skips binary/config validation (essential for worktrees without hashcat-utils). HASHMOB_TEST_REAL=1, HASHVIEW_TEST_REAL=1, WEAKPASS_TEST_REAL=1 enable live API tests.
Git Hooks
Git hooks are managed by prek (v0.3.3+). Install with:
prek install --hook-type pre-push --hook-type post-commit
Hooks are defined in prek.toml using the pre-commit local-repo schema (TOML, not YAML):
[[repos]]
repo = "local"
[[repos.hooks]]
id = "ruff"
entry = "uv run ruff check hate_crack"
language = "system"
stages = ["pre-push"]
pass_filenames = false
always_run = true
Active hooks:
- pre-push: ruff, ty, pytest, pytest-lima
- post-commit: audit-docs
Note: prek 0.3.3 expects repos = [...] at the top level. The old [hooks.<stage>] commands = [...] format is not supported and will fail with missing field 'repos'.
Documentation Auditing
Automatic documentation audits run after each commit via prek hooks.
# Manually audit a commit
bash .claude/audit-docs.sh HEAD
bash .claude/audit-docs.sh <commit_sha>
# Check the last N commits for documentation gaps
bash .claude/check-docs.sh 5
See .claude/DOCUMENTATION-AUDIT.md for details on the audit system.
Worktree Policy
Every agent MUST work in a dedicated git worktree - never edit files directly in the main repo checkout. This prevents conflicts when multiple agents run in parallel.
Setup
# Create a worktree under /tmp (keeps the parent directory clean)
git worktree add /tmp/hate_crack-<task-name> -b <branch-name>
cd /tmp/hate_crack-<task-name>
# Install dev dependencies in the new worktree
uv sync --dev
# Run tests in the worktree
HATE_CRACK_SKIP_INIT=1 uv run pytest -v
Rules
- Always create a worktree before making any file changes:
git worktree add /tmp/hate_crack-<task> -b <branch> - All file edits happen inside the worktree directory, not the main repo
- Run tests and lint inside the worktree before merging
- Merge back via PR or
git mergefrom the main worktree - Clean up when done:
git worktree remove /tmp/hate_crack-<task>
Architecture
Module Map
hate_crack.py(root) - Entry point, menu registration, proxy tohate_crack.mainhate_crack/main.py- Core logic (~3700 lines): hashcat subprocess wrappers, config loading, menu display, global statehate_crack/attacks.py- 16 attack handler functions that receivectx(main module ref)hate_crack/api.py- Hashmob, Hashview, Weakpass API integrationshate_crack/cli.py- Argparse helpers, path resolution, logging setuphate_crack/formatting.py- Terminal UI helpers (multi-column list printing)
Three-Layer Attack Pattern
Every attack spans three files with a specific wiring pattern:
-
hate_crack/main.py- Low-level hashcat function (e.g.,hcatBruteForce(hcatHashType, hcatHashFile))- Builds subprocess commands, manages
hcatProcessglobal, handles KeyboardInterrupt - All hashcat invocations follow: build cmd list ->
cmd.extend(shlex.split(hcatTuning))->_append_potfile_arg(cmd)->subprocess.Popen(cmd)
- Builds subprocess commands, manages
-
hate_crack/attacks.py- Menu handler wrapper (e.g.,def brute_force_crack(ctx: Any))- Receives
ctx(the main module itself) via_attack_ctx(), which returnssys.modules['hate_crack.main'] - Handles user prompts, then calls
ctx.hcatBruteForce(ctx.hcatHashType, ctx.hcatHashFile)
- Receives
-
hate_crack.py(root) - Menu registration + dispatcher- Has its own
get_main_menu_options()that maps keys to_attacks.<handler> - Important:
hate_crack.pyhas a DUPLICATE menu mapping separate frommain.py'sget_main_menu_options(). Both must be updated when adding attacks.
- Has its own
Adding a New Attack - Checklist
- Add the hashcat wrapper function in
main.py(e.g.,hcatMyAttack(...)) - Add the handler in
attacks.py(e.g.,def my_attack(ctx: Any)) - Add a dispatcher in
main.py:def my_attack(): return _attacks.my_attack(_attack_ctx()) - Add the print line in
main.py's menu display loop (~line 3807+) - Add the menu entry in
main.py'sget_main_menu_options() - Add the menu entry in
hate_crack.py'sget_main_menu_options()(the duplicate)
hate_crack.py <-> main.py Proxy
hate_crack.py uses __getattr__ to proxy attribute access to hate_crack.main. It syncs mutable globals via _sync_globals_to_main() and _sync_callables_to_main(). Tests load hate_crack.py as CLI_MODULE and exercise both the proxy and direct module paths.
Config System
config.json is resolved from multiple candidate directories (repo root, cwd, ~/.hate_crack, /opt/hate_crack, etc.) and auto-created from config.json.example if missing. Each config key is loaded via try/except with default_config fallback. New config vars need: entry in config.json.example, try/except loading block in main.py (~line 454 area), and path normalization via _normalize_wordlist_setting() if it's a wordlist path.
Path Distinction
hate_path- hate_crack assets directory (hashcat-utils, princeprocessor, masks, PACK). All bundled binaries use this.hcatPath- hashcat installation directory. Only used for the hashcat binary itself.
External Binary Pattern
Binaries are verified at startup via ensure_binary(path, build_dir, name). Non-critical binaries (princeprocessor, hcstat2gen) use try/except around ensure_binary with a warning message. The SKIP_INIT flag bypasses all binary checks.
Testing Patterns
- Menu option tests in
test_ui_menu_options.pyuse monkeypatching againstCLI_MODULE(loaded fromhate_crack.py) - API tests mock
requestsresponses; most are offline-first - conftest.py provides
hc_modulefixture viaload_hate_crack_module()which dynamically imports roothate_crack.pywith SKIP_INIT enabled - E2E tests (
test_e2e_local_install.py,test_docker_script_install.py) are opt-in viaHATE_CRACK_RUN_E2E=1andHATE_CRACK_RUN_DOCKER_TESTS=1