Policy-driven code enforcement for TypeScript projects.
Codepol is a policy enforcement framework for TypeScript that combines ESLint rules, Tree-sitter structural checks, and build-time enforcement. Define custom rules once, enforce them everywhere.
┌────────────────────────────────────────────┐
│ Consumer Codebase │
│ ┌───────────────────┐ ┌─────────────┐ │
│ │ codepol.toml │ │ src/**/*.ts │ │
│ └─────────┬─────────┘ └─────────────┘ │
└─────────────┼──────────────────────────────┘
│
▼
┌────────────────────────────────────────────┐
│ @codepol/core │
│ • Load and parse codepol.toml │
│ • Tree-sitter structural analysis │
│ • Violation detection and formatting │
└─────────────────────┬──────────────────────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ ESLint │ │ esbuild │ │ CLI │
│ Plugin │ │ Plugin │ │ │
└──────────┘ └──────────┘ └──────────┘
| Package | Description |
|---|---|
| @codepol/core | Core policy loading, Tree-sitter checks, and enforcement |
| @codepol/plugin-eslint | ESLint rule with autofix for logger instrumentation |
| @codepol/plugin-esbuild | esbuild plugin for build-time enforcement |
| @codepol/plugin | Logger plugin with Tree-sitter + ESLint capabilities |
| @codepol/cli | Command-line interface for running checks |
# Install the CLI (includes all dependencies)
pnpm add -D @codepol/cli
# Or install individual packages
pnpm add -D @codepol/core @codepol/plugin-eslint @codepol/plugin- Node-capable projects (JS/TS toolchain available): use
@codepol/clivianpx codepol. - Non-Node projects: use the standalone binary bundle (see "Use the Standalone Binary").
- Add a
codepol.tomlfile in your project root. - If you run ESLint directly, assemble the
codepolplugin from resolved rule plugins ineslint.config.*. - Run checks with:
# Run once
npx codepol
# Apply fixes
npx codepol --fix
# Watch mode
npx codepol --watchCodepol auto-discovers codepol.toml from the current directory upward. Use --config if your config is elsewhere.
Create codepol.toml in your project root:
exclude = ["dist/**"]
[[plugins]]
id = "@codepol/plugin"
source = { kind = "builtin" }
[targets.typescript-src]
language = "typescript"
files = ["src/**/*.ts", "src/**/*.tsx"]
exclude = ["**/*.spec.ts", "**/*.test.ts"]
[[rules]]
id = "function-logging"
ruleId = "@codepol/plugin/require-logger-enter-exit"
description = "Ensure all functions have logger instrumentation"
targets = ["typescript-src"]
[rules.args.logger]
identifier = "logger"
enterMethod = "enter"
exitMethod = "exit"
import = { module = "@your-org/logger", named = "logger" }Add to your eslint.config.js if you want direct ESLint integration:
import { eslintPluginCreate } from '@codepol/plugin-eslint';
import {
pluginBuiltinRegister,
policyPluginRulesGet,
providerRulesConfigGet,
} from '@codepol/core';
import codepolBuiltin from '@codepol/plugin';
pluginBuiltinRegister('@codepol/plugin', codepolBuiltin);
const codepol = eslintPluginCreate(await policyPluginRulesGet());
export default [
{
plugins: {
codepol,
},
rules: {
...await providerRulesConfigGet('eslint'),
},
},
];Codepol loads rule-level plugin capabilities from codepol.toml declarations. Built-in plugins resolve from an
in-process registry, while universal custom plugins can run through the subprocess protocol. The CLI uses the
enabled rule plugins to decide which fixes and adapted ESLint rules to run, while Tree-sitter checking continues to
use the policy rules and their associated tree check providers.
The @codepol/plugin-eslint package is a thin adapter that aggregates rules from capability plugins such as
@codepol/plugin. Use eslintPluginCreate(pluginRules) to assemble the ESLint adapter from any set of
CodepolPluginRule instances.
# Using CLI
npx codepol
# With autofix
npx codepol --fix
# Watch mode
npx codepol --watchFor non-Node projects, this is the recommended way to run codepol.
For Node-capable projects, you can use either this binary bundle or npx codepol.
Download binaries from GitHub Releases (or CI artifacts) instead of committing dist-binary to your repo.
The release bundle contains the binary and required WASM files (must stay in the same directory):
codepoltree-sitter.wasmtree-sitter-typescript.wasmtree-sitter-tsx.wasmtree-sitter-python.wasm
Install to ~/.local/bin so codepol is available on PATH:
# Pick a release tag, for example v1.2.3
TAG=v1.2.3
# Download and install to ~/.local/bin
curl -fL "https://github.com/fruitiecutiepie/codepol/releases/download/${TAG}/codepol-binary-${TAG}-linux-x64.tar.gz" \
| tar -xz -C ~/.local/bin
# Alternative: download from a workflow artifact (requires gh auth)
# gh run download <run-id> --name codepol-binary --dir ~/.local/binMake sure ~/.local/bin is on your PATH (most systems include it by default).
Run from your project root:
codepol
# or
codepol --config ./codepol.tomlCodepol enforces custom policy rules defined by plugins. For example, a no-duplicate-exports rule that prevents naming collisions across your codebase:
[[plugins]]
id = "@your-org/plugin"
[plugins.source]
kind = "process"
command = "python3"
args = ["./tools/your_codepol_plugin.py"]
[targets.src]
language = "typescript"
files = ["src/**/*.ts"]
[[rules]]
ruleId = "@your-org/plugin/no-duplicate-exports"
targets = ["src"]
[rules.args]
include = ["function", "class", "type"]This rule uses Tree-sitter to scan all files for exported identifiers, then reports conflicts:
src/auth/UserService.ts:5 - Duplicate export 'UserService' (also in src/legacy/UserService.ts:12)
The built-in @codepol/plugin includes a require-logger-enter-exit rule, but you can create plugins for any structural pattern. See Creating Custom Plugins.
- Getting Started - Step-by-step setup guide
- Creating Custom Plugins - Build custom Codepol plugins
- Policy Schema - Complete configuration reference
- API Reference - Programmatic usage guide
# Install dependencies
pnpm install
# Build all packages
pnpm build
# Run tests
pnpm test
# Type check
pnpm typecheckMIT