Skip to content

Repository files navigation

ruledoc

┌─┐┬ ┬┬  ┌─┐┌┬┐┌─┐┌─┐
├┬┘│ ││  ├┤  │││ ││
┴└─└─┘┴─┘└─┘─┴┘└─┘└─┘

npm version npm downloads CI license node zero dependencies

Extract @rule() annotations from your codebase and generate living business rules documentation.

Business rules are scattered everywhere — constants, guards, validators, middleware. ruledoc finds them all and produces a structured, searchable doc.

Try it

npx ruledoc

Zero install, zero config. Scans ./src and generates your doc instantly.

Install

Once you're sold, add it as a dev dependency:

npm install -D ruledoc

Requires Node.js 20.19.0 or later.

Quick start

1. Annotate your code

// @rule(billing.plans, critical): Free plan is limited to 50 links
export const FREE_PLAN_LINK_LIMIT = 50;

// @rule(auth.session): Session expires after 24h of inactivity
const SESSION_TTL_MS = 24 * 60 * 60 * 1000;

// @rule(redirect.chain, FLEW-234, warning): Max 5 hops to prevent loops
export const MAX_REDIRECT_CHAIN = 5;

2. Run npx ruledoc

3. Get your docBUSINESS_RULES.md + BUSINESS_RULES.json at the root.

Annotation format

// @rule(scope): Description
// @rule(scope.sub): With subscope
// @rule(scope.sub, critical): With severity
// @rule(scope.sub, critical, JIRA-123): With ticket
// @rule(scope.sub, JIRA-123, critical): Order doesn't matter
// @rule(scope.sub, JIRA-123): Ticket only — severity defaults to info
Param Required Description
scope Dotted path like billing.plans, auth.session
severity One of info (default), warning, critical — or custom
ticket Any string like JIRA-123, FLEW-234, #42

Smart detection: severity and ticket can be in any order. ruledoc matches against the known severity list and figures out which is which.

Warnings

ruledoc catches annotation issues without blocking:

◆ ruledoc 28 rules · 5 scopes · 7 critical · 5 warning

  ⚠ auth/session.ts:4 — unknown severity "crtical", did you mean "critical"? (defaulting to info)
  ⚠ billing/limits.ts:12 — empty description

Warnings appear in the terminal, and are included in the generated Markdown and JSON output.

Output formats

Format Flag Description
Markdown md Grouped by scope, TOC, summary table, severity badges, warnings
JSON json Structured tree for tooling, includes warnings
HTML html Standalone dark-themed page with search, scope/severity filters
Context context Flat, token-efficient text for LLM prompts (CLAUDE.md, .cursorrules)
ruledoc --format md,json,html,context

Context format

The context format produces a flat text file optimized for injecting into LLM prompts. Rules are sorted by severity (critical first) and output one per line with file references.

ruledoc --format context

Output (BUSINESS_RULES.context):

# Business Rules (auto-generated by ruledoc — do not edit)
# 28 rules · 5 scopes · Generated 2026-03-16
[critical] billing.plans: Free plan is limited to 50 links (billing/limits.ts:1)
[warning] redirect.chain: Max 5 hops to prevent loops (redirect/handler.ts:12)
[info] auth.session: Session expires after 24h of inactivity (auth/session.ts:3)

Wire it into your LLM context file:

cat BUSINESS_RULES.context >> CLAUDE.md

Configure filtering and limits via ruledoc.config.json:

{
  "formats": ["md", "json", "context"],
  "context": {
    "maxRules": 50,
    "severities": ["critical", "warning"]
  }
}

Interactive diff

Each run compares with the previous JSON output and shows what changed:

◆ ruledoc 28 rules · 5 scopes · 7 critical · 5 warning
  + Refunds must be processed within 48h [critical] billing.refund → billing/limits.ts:22
  - Old trial rule [info] billing.trial → billing/limits.ts:8
  → ./BUSINESS_RULES.md
  → ./BUSINESS_RULES.json

CI check

ruledoc --check

Exits with code 1 if the generated doc differs from the existing file. Combine with --quiet for clean CI output:

ruledoc --check --quiet
# .github/workflows/rules.yml
- run: npx ruledoc --check --quiet

Rule protection

Prevent accidental removal of critical rules:

ruledoc --check --protect critical

When a protected rule is removed, --check exits with code 2:

✗ [critical] billing.plans: Free plan limited to 50 items
    was in billing/limits.ts

✗ ruledoc: 1 critical rule(s) removed — build blocked
  To allow removal, use --allow-removal or add a @rule-removed() comment.

You can protect multiple severity levels:

ruledoc --check --protect critical,warning

Or in config:

{
  "protect": ["critical"]
}

Acknowledging removals

When you intentionally remove a protected rule, add a @rule-removed() annotation to acknowledge it:

// @rule-removed(billing.plans, JIRA-456): Migrated to config service

This tells ruledoc the removal was deliberate and unblocks the build.

Bypassing protection

To skip all protection checks (e.g. during a large refactor):

ruledoc --check --protect critical --allow-removal

History

ruledoc tracks removed rules as tombstones in BUSINESS_RULES_HISTORY.json. This gives you a changelog of what rules existed and when they were removed.

Removed rules also appear in the Markdown output under a "Removed rules" section, with their removal date and acknowledgment details (if any).

To disable history tracking:

ruledoc --no-history

Or in config:

{
  "history": false
}

GitHub Action

Use the built-in composite action to automate rule checking and PR comments:

# .github/workflows/rules.yml
name: Rules
on:
  pull_request:
    branches: [main]

jobs:
  ruledoc:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - uses: fmekkaoui/ruledoc/action@main
        with:
          protect: critical
          allow-removal: 'false'

The action will:

  • Run ruledoc --check to verify docs are up to date
  • Post a PR comment summarizing added/removed rules
  • Fail the check if protected rules are removed without acknowledgment

Ignore & Exclude

ruledoc uses a three-layer ignore system to keep noise out of your docs:

Layer 1 — .gitignore (on by default): Automatically respects your project's .gitignore patterns so build output, node_modules, and vendor code are never scanned.

Layer 2 — Test file exclusion (on by default): Skips *.test.*, *.spec.*, and __tests__/** files since test annotations are usually not real business rules.

Layer 3 — Extra patterns: Add custom glob patterns to exclude additional files.

Config

{
  "extraIgnore": ["**/generated/**", "**/vendor/**"],
  "ignoreTests": true,
  "gitignore": true
}

CLI flags

ruledoc --extra-ignore "**/generated/**,**/vendor/**"
ruledoc --no-ignore-tests    # include test files
ruledoc --no-gitignore       # don't respect .gitignore

The --extra-ignore flag appends to any patterns set in the config file (it never replaces them). The existing --ignore flag for directory names continues to work as before.

CLI

ruledoc [options]

Options:
  -s, --src <dir>             Source directory (default: ./src)
  -o, --output <file>         Output file (default: ./BUSINESS_RULES.md)
  -f, --format <formats>      md,json,html,context (default: md,json)
  -e, --extensions <exts>     .ts,.vue,.py (default: .ts,.tsx,.js,.jsx,.mjs,.cjs,.vue,.svelte)
      --ignore <dirs>         Directories to skip
      --extra-ignore <globs>  Additional glob patterns to exclude (comma-separated)
      --no-ignore-tests       Include test files (*.test.*, *.spec.*, __tests__)
      --no-gitignore          Don't respect .gitignore patterns
  -t, --tag <name>            Annotation tag (default: rule → @rule(...))
      --severities <list>     Severity levels, first is default (default: info,warning,critical)
  -p, --pattern <regex>       Custom regex (overrides --tag)
  -c, --check                 CI mode: exit 1 if docs are stale
      --protect <severities>  Block removal of rules at these severity levels (comma-separated)
      --allow-removal         Bypass all protection checks
  -q, --quiet                 Suppress all output except errors
      --no-history            Don't track removed rules in history file
      --verbose               List every rule found
      --init                  Setup guide and example config
  -h, --help                  Show this help
  -v, --version               Show version

Quiet mode

--quiet suppresses all terminal output except errors. Useful in CI or when piping.

ruledoc --quiet

Verbose mode

--verbose lists every rule found, grouped by scope:

◆ ruledoc 28 rules · 5 scopes · 7 critical

  Auth (7)
    ● [critical] Session expires after 24h → auth/session.ts:3
    ● Password must be 8+ chars → auth/password.ts:1
    ...
  Billing (5)
    ● [critical] Free plan limited to 50 links → billing/limits.ts:1
    ● [warning] Trial lasts 14 days FLEW-102 → billing/limits.ts:7
    ...

Config

Config is loaded in this order (later wins):

  1. ruledoc.config.json
  2. "ruledoc" field in package.json
  3. CLI flags

Only JSON config files are supported — no JS/TS configs (no eval, no code execution).

{
  "src": "./src",
  "output": "./BUSINESS_RULES.md",
  "formats": ["md", "json", "html"],
  "tag": "rule",
  "severities": ["info", "warning", "critical"],
  "extensions": [".ts", ".tsx", ".js", ".jsx", ".vue"],
  "extraIgnore": ["**/generated/**"]
}

Or in package.json:

{
  "ruledoc": {
    "src": "./app",
    "formats": ["md", "json", "html"]
  }
}

Config validation

ruledoc validates the entire config before running and gives clear error messages:

✗ Invalid config:
  • unknown format "xml" — valid formats: md, json, html, context
  • extension "ts" must start with a dot (e.g. ".ts")
  • pattern is not a valid regex: Unterminated character class

Custom tag

Use a different annotation name:

{ "tag": "bizrule" }

Now ruledoc matches @bizrule(...) instead of @rule(...).

Custom severities

{ "severities": ["low", "medium", "high", "blocker"] }

The first value is the default when severity is omitted.

Custom regex

For full control, provide a regex with two capture groups — (1) params inside parens and (2) description:

{
  "pattern": "(?:\\/\\/|#)\\s*@business\\(([^)]+)\\)\\s*:\\s*(.+)"
}

When pattern is set, it overrides tag.

Use with Turborepo

// package.json
{ "scripts": { "rules": "ruledoc" } }
// turbo.json
{
  "tasks": {
    "rules": {
      "inputs": ["src/**/*.ts", "src/**/*.tsx"],
      "outputs": ["BUSINESS_RULES.md", "BUSINESS_RULES.json"]
    }
  }
}

Dev server + rules watcher side by side:

turbo watch dev rules --filter=web

Programmatic API

Use ruledoc as a library in your own tools:

import { extractRules, resolveConfig, generateMarkdown } from "ruledoc";

const config = resolveConfig([], process.cwd());
const { rules, warnings } = extractRules(config);
const md = generateMarkdown(rules, warnings, config.src);

Security

ruledoc is designed to be safe:

  • No eval — config is loaded from JSON only, never executed as code
  • No runtime deps — zero third-party dependencies in production
  • Read-only scanning — source files are never modified
  • No network — everything runs locally

Example

See examples/saas-billing/ for a working demo with ~10 annotated rules across billing, auth, and notification modules.

License

MIT

About

Extract @rule() annotations from code and generate living business rules documentation

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages