Skip to content

Rule API

Archgate rules are TypeScript files that export a plain object typed with satisfies RuleSet. Each rule receives a RuleContext with utilities for searching files, reading content, and reporting violations.

/// <reference path="../rules.d.ts" />
export default {
rules: {
"my-rule-id": {
description: "Human-readable description of what this rule checks",
severity: "error", // optional, defaults to "error"
async check(ctx) {
// Rule logic here
},
},
},
} satisfies RuleSet;

A rules file default-exports a plain object with a rules record keyed by rule ID. Keys become the rule IDs that appear in check output and violation reports. The satisfies RuleSet annotation provides type checking without wrapping in a function call.

type RuleSet = { rules: Record<string, RuleConfig> };

Each rule in the record must conform to the RuleConfig interface.

interface RuleConfig {
description: string;
severity?: Severity;
check: (ctx: RuleContext) => Promise<void>;
}

| Field | Type | Required | Description | | ------------- | ------------------------------------- | -------- | ------------------------------------------------------ | | description | string | Yes | Human-readable description shown in check output | | severity | Severity | No | Default severity for violations. Defaults to "error" | | check | (ctx: RuleContext) => Promise<void> | Yes | Async function containing the rule logic |


The check function receives a RuleContext object with the project state and utility methods.

interface RuleContext {
projectRoot: string;
scopedFiles: string[];
changedFiles: string[];
glob(pattern: string): Promise<string[]>;
grep(file: string, pattern: RegExp): Promise<GrepMatch[]>;
grepFiles(pattern: RegExp, fileGlob: string): Promise<GrepMatch[]>;
readFile(path: string): Promise<string>;
readJSON(path: string): Promise<unknown>;
report: RuleReport;
}
projectRoot: string;

Absolute path to the project root directory (where .archgate/ lives).

scopedFiles: string[];

Files matching the ADR’s files glob patterns from its frontmatter. If the ADR has no files field, this contains all project files. Use this as the primary file list for your rule checks.

changedFiles: string[];

Files that have been modified according to git. By default, this is auto-populated with the branch diff against the detected base branch (e.g., origin/main). When --staged is used, this contains only staged files. When --base <ref> is used, this contains all files changed since that ref. Empty when base detection fails or no changes are found. Use this to build cross-file dependency rules (e.g., “if file A changed, file B must also change”).

report: RuleReport;

The reporting interface for recording violations, warnings, and informational messages. See RuleReport below.

glob(pattern: string): Promise<string[]>;

Find files matching a glob pattern relative to the project root. Returns an array of file paths. Files ignored by .gitignore are excluded by default. Set respectGitignore: false in the ADR frontmatter to include them.

const testFiles = await ctx.glob("tests/**/*.test.ts");
grep(file: string, pattern: RegExp): Promise<GrepMatch[]>;

Search a single file for lines matching a regular expression. Returns an array of GrepMatch objects with file path, line number, column, and matched content.

const matches = await ctx.grep(file, /console\.error\(/);
grepFiles(pattern: RegExp, fileGlob: string): Promise<GrepMatch[]>;

Search multiple files matching a glob pattern for lines matching a regular expression. Combines glob and grep into a single call. Files ignored by .gitignore are excluded by default. Set respectGitignore: false in the ADR frontmatter to include them.

const matches = await ctx.grepFiles(/TODO:/i, "src/**/*.ts");
readFile(path: string): Promise<string>;

Read the contents of a file as a string. The path is relative to the project root.

const content = await ctx.readFile("src/config.ts");
readJSON(path: string): Promise<unknown>;

Read and parse a JSON file. The path is relative to the project root. Returns the parsed value as unknown — cast to the expected type in your rule.

const pkg = (await ctx.readJSON("package.json")) as {
dependencies?: Record<string, string>;
};

The reporting interface for recording check results. Each method accepts a detail object describing the issue.

interface RuleReport {
violation(detail: ReportDetail): void;
warning(detail: ReportDetail): void;
info(detail: ReportDetail): void;
}
report.violation(detail: ReportDetail): void;

Report a rule violation. Violations cause the check to fail with exit code 1. Use for hard constraints that must not be merged.

report.warning(detail: ReportDetail): void;

Report a warning. Warnings appear in check output but do not cause the check to fail. Use for non-blocking guidance.

report.info(detail: ReportDetail): void;

Report an informational message. Does not affect the check exit code. Use for suggestions or notes.

The detail object passed to violation, warning, and info.

interface ReportDetail {
message: string;
file?: string;
line?: number;
endLine?: number;
endColumn?: number;
fix?: string;
}

| Field | Type | Required | Description | | ----------- | -------- | -------- | ------------------------------------------------------------------- | | message | string | Yes | Human-readable description of the issue | | file | string | No | File path where the issue was found | | line | number | No | Start line number (1-based) | | endLine | number | No | End line number (1-based) — for precise editor range highlighting | | endColumn | number | No | End column number (0-based) — for precise editor range highlighting | | fix | string | No | Suggested fix or remediation action |

When endLine and endColumn are provided, editors (VS Code, Cursor) can highlight the exact expression that violates the rule, rather than the entire line. If omitted, the full line at line is highlighted.


Returned by ctx.grep() and ctx.grepFiles().

interface GrepMatch {
file: string;
line: number;
column: number;
content: string;
}

| Field | Type | Description | | --------- | -------- | ----------------------------------------- | | file | string | Project-relative path to the matched file | | line | number | Line number of the match (1-based) | | column | number | Column number of the match (1-based) | | content | string | Full content of the matched line |


type Severity = "error" | "warning" | "info";

| Value | Exit code impact | Description | | ----------- | ---------------- | ------------------------------- | | "error" | Causes exit 1 | Hard constraint, blocks merges | | "warning" | No impact | Non-blocking guidance | | "info" | No impact | Informational, suggestions only |


The internal representation of a reported issue, used in check output and JSON results.

interface ViolationDetail {
ruleId: string;
adrId: string;
message: string;
file?: string;
line?: number;
endLine?: number;
endColumn?: number;
fix?: string;
severity: Severity;
}

| Field | Type | Description | | ----------- | ---------- | ------------------------------------------------------ | | ruleId | string | Rule ID from the rules object key | | adrId | string | ADR ID from the frontmatter | | message | string | Human-readable description | | file | string? | File path where the issue was found | | line | number? | Start line number (1-based) | | endLine | number? | End line (1-based) — for precise editor highlighting | | endColumn | number? | End column (0-based) — for precise editor highlighting | | fix | string? | Suggested fix | | severity | Severity | Effective severity of this violation |


Violations can be suppressed in source code using archgate-ignore comments. The engine handles this automatically — rules do not need any special logic.

// archgate-ignore ARCH-006/no-unapproved-deps legacy dep, migration planned
import chalk from "chalk";

A reason is required. File-level suppression uses archgate-ignore-file. Stack multiple comments to suppress more than one rule on the same line. See Opt-out directives for full details and custom directive patterns.