diff --git a/home/.claude/CLAUDE.md b/home/.claude/CLAUDE.md new file mode 100644 index 0000000..249503b --- /dev/null +++ b/home/.claude/CLAUDE.md @@ -0,0 +1,62 @@ +# Personal conventions (Python homelab) + +## Stack defaults +- **Package + venv:** uv. Every project has `pyproject.toml` + `uv.lock` checked in. + `uv init`, `uv add `, `uv sync`, `uv run `. Never `pip install` outside a uv-managed venv. +- **Python version:** pinned via `.python-version` in each project (uv reads it). Use latest stable unless a dep blocks. +- **Linter + formatter:** ruff (replaces black + flake8 + isort). Config in `pyproject.toml` under `[tool.ruff]`. +- **Type checker:** ty (Astral, replaces mypy). Config in `pyproject.toml` under `[tool.ty]`. +- **Tests:** pytest. Layout: `src//` + `tests/` at repo root. Shared fixtures in `tests/conftest.py`. +- **Pre-commit:** every project runs ruff (format + lint) + ty on commit. Config in `.pre-commit-config.yaml`. + +## Project bootstrap (preferred) +```bash +mkproj # cd ~/work/ && uv init +uv add --dev pytest ruff ty pre-commit +pre-commit install +git add . && git commit -m "chore: scaffold" +``` + +## pyproject.toml snippets (use these defaults) +```toml +[tool.ruff] +line-length = 100 +target-version = "py313" + +[tool.ruff.lint] +select = ["E", "F", "I", "UP", "B", "SIM", "RUF"] +ignore = ["E501"] # line length handled by formatter + +[tool.ty] +strict = true + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "-q --strict-markers --strict-config" +``` + +## .pre-commit-config.yaml (use this exact set) +```yaml +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.9.0 + hooks: + - id: ruff + - id: ruff-format + - repo: https://github.com/astral-sh/ty-pre-commit + rev: 0.0.1a8 + hooks: + - id: ty-check +``` + +## Git practices +- Conventional commit prefixes: `feat:`, `fix:`, `chore:`, `refactor:`, `docs:`, `test:`. One concern per commit; keep messages in imperative mood. +- Branch off `main`. No long-lived feature branches; rebase before merge. +- **Never** `--force` push to `main`, never `reset --hard` other people's work. (Both denied in my permissions.) +- Run `uv run pytest && uv run ruff check && uv run ty check` before pushing. pre-commit hook should catch most of it locally first. + +## Don'ts +- No `pip install` directly. Use `uv` so the lockfile is honored. +- No mocking the database in integration tests if a real one is reachable (uv-managed sqlite is fine). +- Don't add deps for trivial helpers — three lines of stdlib beats a transitive tree. +- Don't write code comments that restate what the code does. Comment only on non-obvious *why*. diff --git a/home/.claude/settings.json b/home/.claude/settings.json new file mode 100644 index 0000000..08fd6fe --- /dev/null +++ b/home/.claude/settings.json @@ -0,0 +1,50 @@ +{ + "permissions": { + "allow": [ + "Read(*)", + "Bash(ls:*)", + "Bash(find:*)", + "Bash(grep:*)", + "Bash(rg:*)", + "Bash(head:*)", + "Bash(tail:*)", + "Bash(wc:*)", + "Bash(git status:*)", + "Bash(git diff:*)", + "Bash(git log:*)", + "Bash(git show:*)", + "Bash(git add:*)", + "Bash(git commit:*)", + "Bash(git push:*)", + "Bash(git pull:*)", + "Bash(git checkout:*)", + "Bash(git switch:*)", + "Bash(git branch:*)", + "Bash(git stash:*)", + "Bash(git fetch:*)", + "Bash(git restore:*)", + "Bash(uv:*)", + "Bash(uv run:*)", + "Bash(uv add:*)", + "Bash(uv sync:*)", + "Bash(uv pip:*)", + "Bash(pytest:*)", + "Bash(uv run pytest:*)", + "Bash(ruff:*)", + "Bash(uv run ruff:*)", + "Bash(ty:*)", + "Bash(uv run ty:*)", + "Bash(pre-commit:*)", + "Bash(uv run pre-commit:*)" + ], + "deny": [ + "Bash(sudo rm:*)", + "Bash(rm -rf /:*)", + "Bash(rm -rf /*)", + "Bash(rm -rf ~:*)", + "Bash(git push --force:*)", + "Bash(git push -f:*)", + "Bash(git reset --hard:*)" + ] + } +} diff --git a/home/.zshrc b/home/.zshrc index 12e359b..e8e5aec 100644 --- a/home/.zshrc +++ b/home/.zshrc @@ -39,5 +39,5 @@ alias da='docker attach' alias dl='docker logs -f --tail=200' alias de='docker exec -it' -# Claude Code — workspaces are isolated; skip the trust + permission prompts -alias claude='claude --dangerously-skip-permissions' +# Claude Code — sensible permission allowlist lives in ~/.claude/settings.json; +# add --dangerously-skip-permissions per-call when you want full trust.