# =============================================================================
# Lint namespace — one command per language, degrading to a clear skip
# message (never a failure) when that language isn't present in this repo.
# Included from the root Taskfile as `lints:`, so `task lints:go` /
# `task lints:py` / `task lints:js` / `task lints:docs` all work individually,
# and the root `task lint` fans out to all four in one shot.
# Why these four commands and not more: each one already consolidates
# several underlying checkers into a single tool/entrypoint —
# go -> golangci-lint (wraps go vet, staticcheck, errcheck, revive,
# gofmt, unused, ... under one .golangci.yml), or scripts/lint_go.sh
# from the go-standards skill if vendored into this repo (adds
# house rules: function-size, %w wrapping, no naked go func(),
# py -> ruff check + ruff format --check (replaces flake8, isort,
# pyupgrade, pydocstyle, and black in one Rust binary), plus mypy
# for the type-checking ruff doesn't do.
# js -> scripts/check_frontend.sh from the senior-frontend skill if
# vendored (oxlint — 50-100x faster than eslint, with a custom
# no-unnecessary-react-import JS plugin — falling back to eslint,
# plus tsc --noEmit and a tsconfig strict-mode gate), or a bare
# oxlint/eslint + tsc fallback if the script isn't vendored. Biome
# runs afterward for CSS/format if installed.
# docs -> the technical-writing skill's scripts/lint.py, itself a single
# dispatcher (lints.toml-driven) over readability, Markdown
# mechanics, Mermaid structure, and prose (Vale/Proselint/codespell
# run inside it, advisory-only).
# This file does not try to reduce the number of underlying engines further —
# that's not possible across genuinely different languages. It reduces
# *entrypoints* to one per language, and the parent Taskfile reduces that to
# one command total: `task lint`.
# =============================================================================
desc: Go — scripts/lint_go.sh if vendored, else golangci-lint, else go vet; skipped if no go.mod
echo " (no go.mod — skipping Go lint)"
if [ -x scripts/lint_go.sh ]; then
./scripts/lint_go.sh ./... || fail=1
elif command -v golangci-lint >/dev/null 2>&1; then
golangci-lint run ./... || fail=1
echo " (golangci-lint not installed — falling back to go vet only)"
desc: Python — ruff check + ruff format --check + mypy; skipped if no pyproject.toml or ruff isn't installed
if [ ! -f pyproject.toml ]; then
echo " (no pyproject.toml — skipping Python lint)"
if ! command -v ruff >/dev/null 2>&1; then
echo " (ruff not installed — skipping Python lint; pip install ruff)"
ruff format --check . || fail=1
if command -v mypy >/dev/null 2>&1; then
echo " (mypy not installed — skipping type check)"
desc: JS/TS/React — scripts/check_frontend.sh if vendored, else oxlint/eslint + tsc; skipped if no package.json
if [ ! -f package.json ]; then
echo " (no package.json — skipping JS/TS lint)"
if [ -x scripts/check_frontend.sh ]; then
./scripts/check_frontend.sh . || fail=1
elif [ -x node_modules/.bin/oxlint ]; then
node_modules/.bin/oxlint . || fail=1
if [ -x node_modules/.bin/tsc ]; then node_modules/.bin/tsc --noEmit || fail=1; fi
elif [ -x node_modules/.bin/eslint ]; then
node_modules/.bin/eslint . || fail=1
if [ -x node_modules/.bin/tsc ]; then node_modules/.bin/tsc --noEmit || fail=1; fi
echo " (no oxlint/eslint installed)"
if [ -x node_modules/.bin/biome ]; then
node_modules/.bin/biome check . || fail=1
desc: Markdown/MDX/Mermaid/prose via the technical-writing skill's dispatcher; skipped if the skill isn't vendored under .claude/skills
lint_py=".claude/skills/technical-writing/scripts/lint.py"
if [ ! -f "$lint_py" ]; then
echo " (technical-writing skill not vendored at .claude/skills — skipping docs lint)"
python3 "$lint_py" --git-changed