AxeForge/structlint
Directory structure & file naming linter

Stop directory drift before it ships.

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.

Sample project, one .structlint.yaml run
  • myapp/
  • cmd/
  • main.go
  • internal/
  • domain/
  • db/
  • go.mod
  • .env.local disallowed_file_pattern
1 violation found — disallowed_file_pattern on .env.local
How it works

One YAML file, five kinds of rules

Every 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.

Directory rules

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.

File naming rules

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 & required groups

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.

Import boundaries

Language-aware import parsing enforces rules like "internal/domain/** cannot import internal/db/**" — across Go, JavaScript, TypeScript, and Python in the same config.

Ships everywhere

Runs the same way in every pipeline

One binary, four ways to wire it in — pick based on how much of the CI you want to own.

IntegrationSetupOutput
GitHub ActionAxeForging/structlint@main, no Go install--format github annotations, optional PR comments
Setup actionAxeForging/structlint/setup@v0.6.1, install onlyVerified binary on PATH for a custom pipeline step
Pre-commit hookstructlint hook install — detects lefthook, pre-commit, or raw gitvalidate --staged --silent, idempotent
Code scanningAny CI runner--format sarif
Commands

The fix loop

01

init --infer

Walks 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.

02

validate

Checks 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.

03

suggest

Runs 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.

04

hook install

Merges a validate --staged --silent call into lefthook, pre-commit, or a raw git hook. Idempotent — safe to run twice.

Use cases

Why teams reach for structlint

Structure drifting across the team

Allow/disallow rules flag stray top-level folders and abandoned experiments instead of letting them accumulate.

structlint validate

A secret almost got committed

disallowed file patterns catch *.env*, *.log, and .DS_Store before they're the thing everyone regrets pushing.

structlint validate --staged --silent

An AI agent keeps misplacing files

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

Domain code importing the DB layer

Language-aware boundaries rules catch forbidden imports across Go, JS, TS, and Python at commit time, not at architecture review.

structlint validate
Install

One line to get started

Install the binary

curl -fsSL https://raw.githubusercontent.com/AxeForging/structlint/main/install.sh | sh

Generate a starter configstructlint init --infer

Validatestructlint validate

Full instructions on GitHub