structlint checks a project's directory layout and file names against a
.structlint.yaml you write once: which directories are
allowed, which files are forbidden, which files must exist, where each
kind of file has to live, and which layers are allowed to import which.
Wire it into a pre-commit hook or CI and every commit either matches the
shape you defined, or fails with an exit code and a stable violation code.
.structlint.yaml rundisallowed_file_pattern on .env.localEvery check maps to a section of .structlint.yaml and one of 13 frozen violation codes — never one-off prose you have to keep re-parsing.
allowedPaths allow-lists directories glob by glob; anything outside it is unallowed_directory. disallowedPaths forbids trees like vendor/** outright, and requiredPaths fails if cmd/ or internal/ never showed up.
Same shape, one level down: *.env* and *.log are blocked outright, only extensions on allowed pass, and required guarantees go.mod or README.md actually exist somewhere in the tree.
placement pins a file kind to a root — *.sql must live under migrations/**. requiredGroups goes further: every cmd/* directory must contain a main.go, or one of Makefile / Taskfile.yml / justfile must exist at all.
Language-aware import parsing enforces rules like "internal/domain/** cannot import internal/db/**" — across Go, JavaScript, TypeScript, and Python in the same config.
One binary, four ways to wire it in — pick based on how much of the CI you want to own.
| Integration | Setup | Output |
|---|---|---|
| GitHub Action | AxeForging/structlint@main, no Go install | --format github annotations, optional PR comments |
| Setup action | AxeForging/structlint/setup@v0.6.1, install only | Verified binary on PATH for a custom pipeline step |
| Pre-commit hook | structlint hook install — detects lefthook, pre-commit, or raw git | validate --staged --silent, idempotent |
| Code scanning | Any CI runner | --format sarif |
init --inferWalks the tree and writes a .structlint.yaml that matches what's actually there, so validate passes on day one. Tighten the rules from a working baseline instead of drowning in violations.
validateChecks directory structure, file naming, placement, required groups, and import boundaries in one pass. Exit code 1 at the first violation, so it drops straight into CI or a pre-commit hook.
suggestRuns the same engine, then proposes fixes: a unified diff for config additions, git mv commands for placement violations. Print-only, exits 0 — never writes on its own.
hook installMerges a validate --staged --silent call into lefthook, pre-commit, or a raw git hook. Idempotent — safe to run twice.
Allow/disallow rules flag stray top-level folders and abandoned experiments instead of letting them accumulate.
structlint validate
disallowed file patterns catch *.env*, *.log, and .DS_Store before they're the thing everyone regrets pushing.
structlint validate --staged --silent
structlint ships its own agent skill (skills/structlint/SKILL.md), so coding agents read the same allow/disallow rules a human would, instead of guessing.
structlint suggest --format json
Language-aware boundaries rules catch forbidden imports across Go, JS, TS, and Python at commit time, not at architecture review.
structlint validate
Install the binary
curl -fsSL https://raw.githubusercontent.com/AxeForging/structlint/main/install.sh | sh
Generate a starter config — structlint init --infer
Validate — structlint validate