This guide provides essential information for agentic coding agents working with the Layerguard codebase.
Layerguard is a framework-agnostic CLI tool that enforces architectural layer boundaries in TypeScript/JavaScript projects. It helps maintain clean architecture by preventing inappropriate dependencies between layers.
# Build the project
pnpm build
# Watch mode for development
pnpm dev
# Type checking
pnpm typecheck# Run ESLint
pnpm lint
# Run linter with auto-fix
pnpm lint -- --fix# Run all unit tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run tests with coverage report
pnpm test:coverage
# Run self-check (layerguard checking itself)
pnpm test:self
# Run a single test file
pnpm test -- tests/unit/cli/check.test.ts
# Run tests matching a pattern
pnpm test -- -t="checkEdge"
# Run a single test suite
pnpm test -- tests/unit/enforcer/checker.test.ts -t="FlowChecker"- Use ES modules with
.jsextensions in import specifiers - Group imports in this order:
- Node.js built-ins
- External packages
- Internal modules (relative paths)
- Use type-only imports when importing only for types:
import type { Type } from './module.js' - Avoid wildcard imports unless necessary
- Use 2 spaces for indentation (no tabs)
- Prefer single quotes for strings
- Always use semicolons
- Trailing commas in multi-line objects/arrays
- Maximum line length of 100 characters
- Use TypeScript for all source files
- Enable strict mode (all strict options)
- Prefer interfaces over types for object shapes
- Use
typealiases for unions, primitives, and tuples - Explicitly type all function parameters and return values
- Use
unknowninstead ofanywhen the type is truly unknown
- Use camelCase for variables and functions
- Use PascalCase for classes, interfaces, and types
- Use UPPER_SNAKE_CASE for constants
- Use descriptive names - prefer
userRepositoryoverrepo - Prefix boolean variables with
is,has, orshouldwhen appropriate - Use verbs for function names (
getUser,validateConfig)
- Always handle errors appropriately - no silent failures
- Create custom error classes that extend
Errorfor specific error types - Include meaningful error messages with context
- Use
instanceofchecks when catching specific error types - Log errors with sufficient context for debugging
- Export all public APIs with JSDoc comments
- Document complex logic with inline comments
- Use clear, concise language in comments
- Keep documentation up to date with code changes
- Follow the existing modular structure with clear separation of concerns
- Use dependency injection where appropriate
- Prefer pure functions and immutable data when possible
- Keep modules focused on a single responsibility
- Use the existing config/loader pattern for loading configuration files
src/- Main source codesrc/cache/- Caching for incremental checkingsrc/cli/- Command line interfacesrc/config/- Configuration loading and validationsrc/enforcer/- Architecture rule enforcementsrc/eslint/- ESLint integrationsrc/output/- Result formatting and outputsrc/parser/- File parsing and dependency extractionsrc/plugins/- Framework pluginssrc/workspace/- Monorepo workspace detectiontests/- Unit tests (mirrors src/ structure)tests/fixtures/- Test fixtures and example projects
- Use Vitest for all tests
- Place test files in
tests/unit/mirroring thesrc/structure (e.g.,src/cli/check.ts→tests/unit/cli/check.test.ts) - Use descriptive test names that explain the expected behavior
- Test both positive and negative cases
- Mock external dependencies
- Use test fixtures for complex scenarios
- Test edge cases and error conditions
- Use
console.logfor temporary debugging (remove before committing) - Use the
--no-cacheflag when testing to ensure fresh runs
- Update version in
package.json - Run
pnpm buildto ensure build works - Run all tests with
pnpm test - Commit changes
- Create git tag with version
- Publish to npm with
pnpm publish
The project supports various frameworks through plugins. When adding new framework support:
- Create a new plugin in
src/plugins/ - Implement the
FrameworkPlugininterface - Add the framework to the config type definitions
- Add tests for the new framework
- Update documentation
- Use caching where appropriate (see existing cache implementations)
- Avoid expensive operations in hot paths
- Use incremental processing when possible
- Profile performance with large codebases