ln-646-project-structure-auditor
Checks file hygiene, ignore files, framework conventions, domain/layer organization, naming. Use when auditing project structure.
Best use case
ln-646-project-structure-auditor is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Checks file hygiene, ignore files, framework conventions, domain/layer organization, naming. Use when auditing project structure.
Teams using ln-646-project-structure-auditor should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/ln-646-project-structure-auditor/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How ln-646-project-structure-auditor Compares
| Feature / Agent | ln-646-project-structure-auditor | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Checks file hygiene, ignore files, framework conventions, domain/layer organization, naming. Use when auditing project structure.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
> **Paths:** File paths (`shared/`, `references/`, `../ln-*`) are relative to skills repo root. If not found at CWD, locate this SKILL.md directory and go up one level for repo root. If `shared/` is missing, fetch files via WebFetch from `https://raw.githubusercontent.com/levnikolaevich/claude-code-skills/master/skills/{path}`.
# Project Structure Auditor
**Type:** L3 Worker
L3 Worker that audits the physical directory structure of a project against framework-specific conventions and hygiene best practices.
## Purpose & Scope
- Auto-detect tech stack and apply framework-specific structure rules
- Audit 5 dimensions: file hygiene, ignore files, framework conventions, domain/layer organization, naming
- Detect project rot: leftover artifacts, inconsistent naming, junk drawer directories
- Complement code-level layer analysis with physical structure analysis
- Score and report findings per standard `audit_scoring.md` formula
- Output: file-based report to `{output_dir}/646-structure[-{domain}].md`
**Out of Scope:**
- Code-level layer boundary violations (import analysis)
- Platform artifact cleanup (removal of files)
- Structure migration (creation/movement of directories)
- Dependency vulnerability scanning
## Input
```
- codebase_root: string # Root directory to scan
- output_dir: string # e.g., ".hex-skills/runtime-artifacts/runs/{run_id}/audit-report"
# Domain-aware (optional, from coordinator)
- domain_mode: "global" | "domain-aware" # Default: "global"
- current_domain: string # e.g., "users", "billing" (only if domain-aware)
- scan_path: string # e.g., "src/users/" (only if domain-aware)
```
## Workflow
**MANDATORY READ:** Load `shared/references/two_layer_detection.md` for detection methodology.
### Phase 1: Detect Tech Stack
**MANDATORY READ:** Load `../ln-700-project-bootstrap/references/stack_detection.md` -- use Detection Algorithm, Frontend Detection, Backend Detection, Structure Detection.
```
scan_root = scan_path IF domain_mode == "domain-aware" ELSE codebase_root
# Priority 1: Read docs/project/tech_stack.md if exists
IF exists(docs/project/tech_stack.md):
tech_stack = parse(tech_stack.md)
# Priority 2: Auto-detect from project files
ELSE:
Check package.json -> React/Vue/Angular/Express/NestJS
Check *.csproj/*.sln -> .NET
Check pyproject.toml/requirements.txt -> Python/FastAPI/Django
Check go.mod -> Go
Check Cargo.toml -> Rust
Check pnpm-workspace.yaml/turbo.json -> Monorepo
tech_stack = {
language: "typescript" | "python" | "csharp" | "go" | ...,
framework: "react" | "fastapi" | "aspnetcore" | ...,
structure: "monolith" | "clean-architecture" | "monorepo" | ...
}
```
### Phase 2: File Hygiene Audit
**MANDATORY READ:** Load `references/structure_rules.md` -- use "File Hygiene Rules" section. Also reference: `../ln-724-artifact-cleaner/references/platform_artifacts.md` (Platform Detection Matrix, Generic Prototype Artifacts).
```
# Check 2.1: Build artifacts tracked in git
FOR EACH artifact_dir IN structure_rules.build_artifact_dirs:
IF Glob("{scan_root}/**/{artifact_dir}"):
findings.append(severity: "HIGH", issue: "Build artifact directory in repo",
location: path, recommendation: "Add to .gitignore, remove from tracking")
# Check 2.2: Temp/log files
FOR EACH pattern IN structure_rules.temp_junk_patterns:
IF Glob("{scan_root}/**/{pattern}"):
findings.append(severity: "MEDIUM", ...)
# Check 2.3: Platform remnants (from platform_artifacts.md)
FOR EACH platform IN [Replit, StackBlitz, CodeSandbox, Glitch]:
IF platform indicator files found:
findings.append(severity: "MEDIUM", issue: "Platform remnant: {file}",
principle: "File Hygiene / Platform Artifacts")
# Check 2.4: Multiple lock files
lock_files = Glob("{scan_root}/{package-lock.json,yarn.lock,pnpm-lock.yaml,bun.lockb}")
IF len(lock_files) > 1:
findings.append(severity: "HIGH", issue: "Multiple lock files: {lock_files}",
recommendation: "Keep one lock file matching your package manager")
# Check 2.6: Large binaries tracked by git
FOR EACH file IN Glob("{scan_root}/**/*.{zip,tar,gz,rar,exe,dll,so,dylib,jar,war}"):
findings.append(severity: "MEDIUM", issue: "Binary file tracked: {file}",
recommendation: "Use Git LFS or remove from repository")
```
### Phase 3: Ignore File Quality
**MANDATORY READ:** Load `references/structure_rules.md` -- use "Ignore File Rules" section. Also reference: `../ln-733-env-configurator/references/gitignore_secrets.template` (secrets baseline), `../ln-731-docker-generator/references/dockerignore.template` (dockerignore baseline).
```
# Check 3.1: .gitignore exists
IF NOT exists(.gitignore):
findings.append(severity: "HIGH", issue: "No .gitignore file")
ELSE:
content = Read(.gitignore)
# Check 3.1a: Stack-specific entries present
required_entries = get_required_gitignore(tech_stack)
FOR EACH entry IN required_entries:
IF entry NOT covered in .gitignore:
findings.append(severity: "MEDIUM", issue: ".gitignore missing: {entry}")
# Check 3.1b: Secrets protection (compare with gitignore_secrets.template)
secrets_patterns = [".env", ".env.local", "*.pem", "*.key", "secrets/"]
FOR EACH pattern IN secrets_patterns:
IF pattern NOT in .gitignore:
findings.append(severity: "HIGH", issue: ".gitignore missing secrets: {pattern}",
principle: "Ignore Files / Secrets")
# Check 3.1c: IDE/OS entries
ide_patterns = [".vscode/", ".idea/", "*.swp", ".DS_Store", "Thumbs.db"]
missing_ide = [p for p in ide_patterns if p NOT covered in .gitignore]
IF len(missing_ide) > 2:
findings.append(severity: "LOW", issue: ".gitignore missing IDE/OS: {missing_ide}")
# Check 3.2: .dockerignore
IF exists(Dockerfile) AND NOT exists(.dockerignore):
findings.append(severity: "MEDIUM", issue: "Dockerfile exists but no .dockerignore",
recommendation: "Create .dockerignore to reduce build context")
ELIF exists(.dockerignore):
FOR EACH required IN [node_modules, .git, .env, "*.log"]:
IF required NOT in .dockerignore:
findings.append(severity: "LOW", ...)
```
### Phase 4: Framework Convention Compliance
**MANDATORY READ:** Load `references/structure_rules.md` -- use framework-specific section matching detected tech_stack.
```
rules = get_framework_rules(tech_stack, structure_rules.md)
# Returns: {expected_dirs, forbidden_placements, co_location_rules}
# Check 4.1: Expected directories exist
FOR EACH dir IN rules.expected_dirs WHERE dir.required == true:
IF NOT exists(dir.path):
findings.append(severity: "MEDIUM", issue: "Expected directory missing: {dir.path}",
principle: "Framework Convention / {tech_stack.framework}")
# Check 4.2: Source code in wrong locations
FOR EACH rule IN rules.forbidden_placements:
matches = Glob(rule.glob_pattern, scan_root)
FOR EACH match IN matches:
IF match NOT IN rules.exceptions:
findings.append(severity: "HIGH", issue: "Source file in wrong location: {match}",
recommendation: "Move to {rule.expected_location}")
# Check 4.3: Co-location rules (React feature folders)
IF tech_stack.framework IN ["react", "vue", "angular", "svelte"]:
component_dirs = Glob("{scan_root}/**/components/*/")
colocation_count = 0
FOR EACH dir IN component_dirs:
has_test = Glob("{dir}/*.{test,spec}.{ts,tsx,js,jsx}")
IF has_test: colocation_count += 1
# Only flag if project already uses co-location (>50%)
IF colocation_count > len(component_dirs) * 0.5:
FOR EACH dir IN component_dirs:
IF NOT has_test_for(dir):
findings.append(severity: "LOW", issue: "Component missing co-located test: {dir}")
```
### Phase 5: Domain/Layer Organization
```
# Check 5.1: Junk drawer detection
junk_thresholds = structure_rules.junk_drawer_thresholds
FOR EACH dir IN Glob("{scan_root}/**/"):
dir_name = basename(dir)
IF dir_name IN junk_thresholds:
file_count = len(Glob("{dir}/*.*"))
IF file_count > junk_thresholds[dir_name].max_files:
findings.append(severity: junk_thresholds[dir_name].severity,
issue: "Junk drawer directory: {dir} ({file_count} files)",
principle: "Organization / Module Cohesion",
recommendation: "Split into domain-specific modules or feature folders")
# Check 5.2: Root directory cleanliness
root_files = Glob("{scan_root}/*") # Direct children only
source_in_root = [f for f in root_files
if f.ext IN source_extensions AND basename(f) NOT IN allowed_root_files]
IF len(source_in_root) > 0:
findings.append(severity: "MEDIUM", issue: "Source files in project root: {source_in_root}",
recommendation: "Move to src/ or appropriate module directory")
config_in_root = [f for f in root_files if is_config_file(f)]
IF len(config_in_root) > 15:
findings.append(severity: "LOW",
issue: "Excessive config files in root ({len(config_in_root)})",
recommendation: "Move non-essential configs to config/ directory")
# Check 5.3: Consistent module structure across domains
IF domain_mode == "global" AND len(detect_domains(scan_root)) >= 2:
domains = detect_domains(scan_root)
structures = {d.name: set(subdirectory_names(d.path)) for d in domains}
all_subdirs = union(structures.values())
FOR EACH domain IN domains:
missing = all_subdirs - structures[domain]
IF 0 < len(missing) < len(all_subdirs) * 0.5:
findings.append(severity: "LOW",
issue: "Inconsistent domain structure: {domain.name} missing {missing}",
recommendation: "Align domain module structures for consistency")
```
### Phase 6: Naming Conventions
**MANDATORY READ:** Load `references/structure_rules.md` -- use "Naming Convention Rules" section.
```
naming_rules = get_naming_rules(tech_stack)
# Returns: {file_case, dir_case, test_pattern, component_case}
# Check 6.1: File naming consistency
violations = []
FOR EACH file IN Glob("{scan_root}/**/*.{ts,tsx,js,jsx,py,cs,go}"):
expected_case = naming_rules.file_case
IF is_component(file) AND NOT matches_case(basename(file), expected_case):
violations.append(file)
IF len(violations) > 0:
pct = len(violations) / total_source_files * 100
severity = "HIGH" if pct > 30 else "MEDIUM" if pct > 10 else "LOW"
findings.append(severity, issue: "Naming violations: {len(violations)} files ({pct}%)",
principle: "Naming / {naming_rules.file_case}")
# Check 6.2: Directory naming consistency
dirs = get_all_source_dirs(scan_root)
dir_cases = classify_cases(dirs) # Count per case style
dominant = max(dir_cases)
inconsistent = [d for d in dirs if case_of(d) != dominant]
IF len(inconsistent) > 0:
findings.append(severity: "LOW",
issue: "Inconsistent directory naming: {len(inconsistent)} dirs use mixed case")
# Check 6.3: Test file naming pattern
test_files = Glob("{scan_root}/**/*.{test,spec}.{ts,tsx,js,jsx}")
+ Glob("{scan_root}/**/*_test.{py,go}")
IF len(test_files) > 0:
patterns_used = detect_test_patterns(test_files) # .test. vs .spec. vs _test
IF len(patterns_used) > 1:
findings.append(severity: "LOW", issue: "Mixed test naming patterns: {patterns_used}",
recommendation: "Standardize to {dominant_test_pattern}")
```
### Phase 7: Score + Report + Return
**MANDATORY READ:** Load `shared/references/audit_worker_core_contract.md`, `shared/references/audit_scoring.md`, and `shared/templates/audit_worker_report_template.md`.
If `summaryArtifactPath` is present, write JSON summary per `shared/references/audit_summary_contract.md`. Compact text output is fallback only.
```
# 7a: Calculate score via shared formula
# 7b: Build report in memory using the shared audit worker template
# 7c: Populate DATA-EXTENDED with:
# tech_stack
# dimensions.file_hygiene / ignore_files / framework_conventions / domain_organization / naming_conventions
# junk_drawers
# naming_dominant_case
# naming_violations_pct
# 7d: Write report (atomic single Write call)
IF domain_mode == "domain-aware":
Write to {output_dir}/646-structure-{current_domain}.md
ELSE:
Write to {output_dir}/646-structure.md
# 7e: Return summary
Report written: .hex-skills/runtime-artifacts/runs/{run_id}/audit-report/646-structure[-{domain}].md
Score: X.X/10 | Issues: N (C:N H:N M:N L:N)
```
## Scoring
**MANDATORY READ:** Load `shared/references/audit_worker_core_contract.md` and `shared/references/audit_scoring.md`.
Severity mapping:
- **HIGH:** Build artifacts tracked, missing .gitignore, source in wrong location, multiple lock files, missing secrets in .gitignore. **Exception:** Build artifacts in Git LFS -> skip
- **MEDIUM:** Missing framework dirs, junk drawers, temp files, platform remnants, missing stack-specific gitignore entries, naming violations >10%
- **LOW:** IDE/OS patterns missing, inconsistent dir naming, mixed test patterns, minor config issues
## Critical Rules
**MANDATORY READ:** Load `shared/references/audit_worker_core_contract.md`.
- **Auto-detect, never assume:** Always detect tech stack before applying framework rules
- **No false positives on conventions:** Apply framework rules ONLY for detected stack
- **Security-first:** Missing secrets in .gitignore = HIGH
- **Complement, not overlap:** Do NOT check import-level layer violations (separate worker scope)
- **Report only, never modify:** Never move/delete files (separate migration/cleanup workers)
- **Reuse platform detection:** Reference platform artifact patterns from references
- **Co-location awareness:** Only flag missing co-location if project already uses the pattern (>50%)
- **Evidence always:** Include file paths for every finding
## Definition of Done
**MANDATORY READ:** Load `shared/references/audit_worker_core_contract.md`.
- [ ] Tech stack detected (from `docs/project/tech_stack.md` or auto-detection)
- [ ] File hygiene checked: build artifacts, temp files, platform remnants, lock files, binaries
- [ ] Ignore files audited: .gitignore completeness, secrets protection, .dockerignore if Dockerfile present
- [ ] Framework conventions applied: expected dirs, forbidden placements, co-location rules
- [ ] Domain/layer organization checked: junk drawers, root cleanliness, cross-domain consistency
- [ ] Naming conventions validated: file/dir/test naming patterns
- [ ] If domain-aware: all Glob scoped to `scan_path`, findings tagged with domain
- [ ] Score calculated per `audit_scoring.md`
- [ ] Report written to `{output_dir}/646-structure[-{domain}].md` (atomic single Write call)
- [ ] Summary written per contract
## Reference Files
- **Structure rules:** `references/structure_rules.md`
- **Stack detection:** `../ln-700-project-bootstrap/references/stack_detection.md`
- **Platform artifacts:** `../ln-724-artifact-cleaner/references/platform_artifacts.md`
- **Gitignore secrets:** `../ln-733-env-configurator/references/gitignore_secrets.template`
- **Dockerignore baseline:** `../ln-731-docker-generator/references/dockerignore.template`
---
**Version:** 1.0.0
**Last Updated:** 2026-02-28Related Skills
ln-743-test-infrastructure
Sets up test infrastructure with Vitest, xUnit, and pytest. Use when adding testing frameworks and sample tests to a project.
ln-721-frontend-restructure
Scaffolds new React projects or restructures monoliths to component-based architecture. Use when setting up frontend structure.
ln-720-structure-migrator
Scaffolds new or restructures existing projects to Clean Architecture. Use when setting up project structure.
ln-700-project-bootstrap
Bootstraps projects to production-ready structure. Use when creating new or transforming existing projects.
ln-654-resource-lifecycle-auditor
Checks session scope mismatch, missing cleanup, pool config, error path leaks, resource holding. Use when auditing resource lifecycle.
ln-653-runtime-performance-auditor
Checks blocking IO in async, unnecessary allocations, sync sleep, string concat in loops, redundant copies. Use when auditing runtime performance.
ln-652-transaction-correctness-auditor
Checks transaction scope, missing rollback handling, long-held transactions, trigger/notify interaction. Use when auditing transaction correctness.
ln-651-query-efficiency-auditor
Checks redundant fetches, N+1 loops, over-fetching, missing bulk operations, wrong caching scope. Use when auditing query efficiency.
ln-650-persistence-performance-auditor
Coordinates persistence and performance audit across queries, transactions, runtime, and resource lifecycle. Use when auditing data layer performance.
ln-647-env-config-auditor
Checks env var config sync, missing defaults, naming conventions, startup validation. Use when auditing environment configuration.
ln-644-dependency-graph-auditor
Builds dependency graph, detects cycles, validates boundary rules, calculates coupling metrics (Ca/Ce/I). Use when auditing dependency structure.
ln-643-api-contract-auditor
Checks layer leakage in method signatures, missing DTOs, entity leakage to API, inconsistent error contracts. Use when auditing API contracts.