archgate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +105 -0
- package/README.md +237 -0
- package/package.json +72 -0
- package/src/formats/rules.ts +86 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Functional Source License, Version 1.1, ALv2 Future License
|
|
2
|
+
|
|
3
|
+
## Abbreviation
|
|
4
|
+
|
|
5
|
+
FSL-1.1-ALv2
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2025 Archgate
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
28
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
33
|
+
means making the Software available to others in a commercial product or
|
|
34
|
+
service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
65
|
+
the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
74
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
75
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
78
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
79
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
90
|
+
the Apache License, Version 2.0 that is effective on the second anniversary of
|
|
91
|
+
the date we make the Software available. On or after that date, you may use the
|
|
92
|
+
Software under the Apache License, Version 2.0, in which case the following
|
|
93
|
+
will apply:
|
|
94
|
+
|
|
95
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
96
|
+
this file except in compliance with the License.
|
|
97
|
+
|
|
98
|
+
You may obtain a copy of the License at
|
|
99
|
+
|
|
100
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
101
|
+
|
|
102
|
+
Unless required by applicable law or agreed to in writing, software distributed
|
|
103
|
+
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
104
|
+
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
105
|
+
specific language governing permissions and limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Archgate
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
**Enforce Architecture Decision Records as executable rules — for both humans and AI agents.**
|
|
6
|
+
|
|
7
|
+
[](LICENSE.md)
|
|
8
|
+
[](https://github.com/archgate/cli/actions/workflows/release.yml)
|
|
9
|
+
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
Archgate turns your Architecture Decision Records into a governance layer that runs in CI, enforces rules in pre-commit hooks, and feeds live context to AI coding agents — so architectural decisions don't stay in documents, they stay in the code.
|
|
15
|
+
|
|
16
|
+
**Write an ADR once. Enforce it everywhere.**
|
|
17
|
+
|
|
18
|
+
## How it works
|
|
19
|
+
|
|
20
|
+
Archgate has two layers:
|
|
21
|
+
|
|
22
|
+
1. **ADRs as documents** — markdown files with YAML frontmatter stored in `.archgate/adrs/`. Each ADR records a decision: what was decided, why, and what to do and not do.
|
|
23
|
+
2. **ADRs as rules** — each ADR can have a companion `.rules.ts` file that exports automated checks. Archgate runs these checks against your codebase and reports violations.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
.archgate/
|
|
27
|
+
└── adrs/
|
|
28
|
+
├── ARCH-001-command-structure.md # human-readable decision
|
|
29
|
+
├── ARCH-001-command-structure.rules.ts # machine-executable checks
|
|
30
|
+
├── ARCH-002-error-handling.md
|
|
31
|
+
└── ARCH-002-error-handling.rules.ts
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
When a rule is violated, `archgate check` reports the file, line, and which ADR was broken. Exit code 1 means violations — wire it into CI and it blocks merges automatically.
|
|
35
|
+
|
|
36
|
+
**The AI integration layer** is a Claude Code plugin that gives AI agents live access to your ADRs through the MCP server (`archgate mcp`). Agents read the decisions before writing code, validate changes after, and capture new patterns into the governance base. Archgate ships as its own governance subject — its own development is governed by the ADRs in `.archgate/adrs/`.
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
curl -fsSL https://archgate.dev/install.sh | sh
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Installs the standalone binary to `~/.archgate/bin/` and adds it to your PATH.
|
|
45
|
+
|
|
46
|
+
**Requirements:** macOS (arm64) or Linux (x86_64).
|
|
47
|
+
|
|
48
|
+
## Quick start
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# 1. Install
|
|
52
|
+
curl -fsSL https://archgate.dev/install.sh | sh
|
|
53
|
+
|
|
54
|
+
# 2. Initialize governance in your project
|
|
55
|
+
cd my-project
|
|
56
|
+
archgate init
|
|
57
|
+
|
|
58
|
+
# 3. Edit the generated ADR to document a real decision
|
|
59
|
+
# .archgate/adrs/ARCH-001-*.md
|
|
60
|
+
|
|
61
|
+
# 4. Add a companion .rules.ts to enforce it automatically
|
|
62
|
+
# .archgate/adrs/ARCH-001-*.rules.ts
|
|
63
|
+
|
|
64
|
+
# 5. Run checks
|
|
65
|
+
archgate check
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`archgate init` creates the `.archgate/adrs/` directory, an example ADR with a companion rules file to show the pattern, and configures the Claude Code plugin if you use it.
|
|
69
|
+
|
|
70
|
+
## Writing rules
|
|
71
|
+
|
|
72
|
+
A companion `.rules.ts` file exports checks using `defineRules()` from the `archgate` package:
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// .archgate/adrs/ARCH-002-error-handling.rules.ts
|
|
76
|
+
import { defineRules } from "archgate/rules";
|
|
77
|
+
|
|
78
|
+
export default defineRules([
|
|
79
|
+
{
|
|
80
|
+
id: "use-log-error",
|
|
81
|
+
description:
|
|
82
|
+
"Use logError() instead of console.error() for user-facing errors",
|
|
83
|
+
severity: "error",
|
|
84
|
+
async check({ files }) {
|
|
85
|
+
const violations = [];
|
|
86
|
+
for (const file of files) {
|
|
87
|
+
const content = await Bun.file(file).text();
|
|
88
|
+
const lines = content.split("\n");
|
|
89
|
+
lines.forEach((line, i) => {
|
|
90
|
+
if (line.includes("console.error(")) {
|
|
91
|
+
violations.push({
|
|
92
|
+
file,
|
|
93
|
+
line: i + 1,
|
|
94
|
+
message: "Use logError() instead",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return violations;
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
]);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Rules receive the list of files to check (filtered by the ADR's `files` glob if set), and return an array of violations with file paths and line numbers.
|
|
106
|
+
|
|
107
|
+
## Commands
|
|
108
|
+
|
|
109
|
+
### `archgate init`
|
|
110
|
+
|
|
111
|
+
Initialize governance in the current project.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
archgate init
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Creates `.archgate/adrs/` with an example ADR and rules file, and optionally wires up the Claude Code plugin.
|
|
118
|
+
|
|
119
|
+
### `archgate check`
|
|
120
|
+
|
|
121
|
+
Run all automated ADR checks against your codebase.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
archgate check # check all files
|
|
125
|
+
archgate check --staged # check only git-staged files (for pre-commit hooks)
|
|
126
|
+
archgate check --json # machine-readable JSON output
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Exits with code 0 if all checks pass, 1 if any violations are found.
|
|
130
|
+
|
|
131
|
+
### `archgate adr create`
|
|
132
|
+
|
|
133
|
+
Create a new ADR interactively.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
archgate adr create
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Prompts for a title, domain, and optional file glob. Generates a sequential ID (`ARCH-001`, `ARCH-002`, ...) and writes the markdown file.
|
|
140
|
+
|
|
141
|
+
### `archgate adr list`
|
|
142
|
+
|
|
143
|
+
List all ADRs in the project.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
archgate adr list # table output
|
|
147
|
+
archgate adr list --json # JSON output
|
|
148
|
+
archgate adr list --domain backend # filter by domain
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### `archgate adr show <id>`
|
|
152
|
+
|
|
153
|
+
Print a specific ADR.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
archgate adr show ARCH-001
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### `archgate adr update`
|
|
160
|
+
|
|
161
|
+
Update an existing ADR's frontmatter.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
archgate adr update ARCH-001 --title "New Title" --domain backend
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### `archgate mcp`
|
|
168
|
+
|
|
169
|
+
Start the MCP server for AI agent integration.
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
archgate mcp
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Exposes four tools to MCP-compatible clients (Claude Code, Cursor, etc.):
|
|
176
|
+
|
|
177
|
+
| Tool | Description |
|
|
178
|
+
| ----------------- | ----------------------------------------------------------------- |
|
|
179
|
+
| `check` | Run ADR compliance checks, optionally on staged files only |
|
|
180
|
+
| `list_adrs` | List all ADRs with metadata |
|
|
181
|
+
| `review_context` | Get changed files grouped by domain with applicable ADR briefings |
|
|
182
|
+
| `session_context` | Read the current Claude Code session transcript |
|
|
183
|
+
|
|
184
|
+
Also exposes `adr://{id}` resources for reading individual ADRs by ID.
|
|
185
|
+
|
|
186
|
+
### `archgate upgrade`
|
|
187
|
+
|
|
188
|
+
Upgrade to the latest release.
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
archgate upgrade
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### `archgate clean`
|
|
195
|
+
|
|
196
|
+
Remove the CLI cache directory (`~/.archgate/`).
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
archgate clean
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## CI integration
|
|
203
|
+
|
|
204
|
+
Add a check step to your pipeline:
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
# GitHub Actions example
|
|
208
|
+
- name: ADR compliance check
|
|
209
|
+
run: archgate check
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
For pre-commit hooks (using [lefthook](https://github.com/evilmartians/lefthook) or similar):
|
|
213
|
+
|
|
214
|
+
```yaml
|
|
215
|
+
pre-commit:
|
|
216
|
+
commands:
|
|
217
|
+
adr-check:
|
|
218
|
+
run: archgate check --staged
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Claude Code plugin
|
|
222
|
+
|
|
223
|
+
The Claude Code plugin (`archgate:developer`) gives AI agents a full governance workflow:
|
|
224
|
+
|
|
225
|
+
- Reads applicable ADRs before writing code
|
|
226
|
+
- Validates changes after implementation
|
|
227
|
+
- Captures new patterns back into ADRs
|
|
228
|
+
|
|
229
|
+
Install the plugin from [archgate/claude-code-plugin](https://github.com/archgate/claude-code-plugin), then run `archgate:onboard` once in your project to initialize governance.
|
|
230
|
+
|
|
231
|
+
## Contributing
|
|
232
|
+
|
|
233
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and workflow.
|
|
234
|
+
|
|
235
|
+
## License
|
|
236
|
+
|
|
237
|
+
[FSL-1.1-ALv2](LICENSE.md) — free to use, cannot be used to build a competing product.
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "archgate",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "AI governance for software development",
|
|
5
|
+
"readme": "README.md",
|
|
6
|
+
"license": "FSL-1.1-ALv2",
|
|
7
|
+
"homepage": "https://archgate.dev",
|
|
8
|
+
"exports": {
|
|
9
|
+
"./rules": "./src/formats/rules.ts"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src/formats/rules.ts"
|
|
13
|
+
],
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Archgate",
|
|
16
|
+
"url": "https://archgate.dev"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"archgate",
|
|
20
|
+
"cli",
|
|
21
|
+
"framework"
|
|
22
|
+
],
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/archgate/cli/issues"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/archgate/cli.git"
|
|
29
|
+
},
|
|
30
|
+
"os": [
|
|
31
|
+
"darwin",
|
|
32
|
+
"linux"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"cli": "bun run src/cli.ts",
|
|
36
|
+
"check": "bun run src/cli.ts check",
|
|
37
|
+
"lint": "oxlint .",
|
|
38
|
+
"typecheck": "tsc --build",
|
|
39
|
+
"format": "prettier --write .",
|
|
40
|
+
"format:check": "prettier --check .",
|
|
41
|
+
"test": "bun test --timeout 60000",
|
|
42
|
+
"test:watch": "bun test --watch --timeout 60000",
|
|
43
|
+
"validate": "bun run lint && bun run typecheck && bun run format:check && bun test && bun run check",
|
|
44
|
+
"commit": "cz",
|
|
45
|
+
"build": "bun run scripts/build.ts",
|
|
46
|
+
"build:darwin": "bun run scripts/build.ts --target darwin-arm64",
|
|
47
|
+
"build:linux": "bun run scripts/build.ts --target linux-x64",
|
|
48
|
+
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0"
|
|
49
|
+
},
|
|
50
|
+
"type": "module",
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@commander-js/extra-typings": "14.0.0",
|
|
53
|
+
"@modelcontextprotocol/sdk": "1.26.0",
|
|
54
|
+
"inquirer": "12.9.4",
|
|
55
|
+
"zod": "4.3.6"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@commitlint/cli": "19.8.1",
|
|
59
|
+
"@commitlint/config-conventional": "19.8.1",
|
|
60
|
+
"@commitlint/cz-commitlint": "19.8.1",
|
|
61
|
+
"@simple-release/npm": "2.3.0",
|
|
62
|
+
"@types/bun": "1.3.9",
|
|
63
|
+
"commitizen": "4.3.1",
|
|
64
|
+
"conventional-changelog": "7.1.1",
|
|
65
|
+
"conventional-changelog-angular": "8.0.0",
|
|
66
|
+
"oxlint": "1.14.0",
|
|
67
|
+
"prettier": "3.6.2"
|
|
68
|
+
},
|
|
69
|
+
"peerDependencies": {
|
|
70
|
+
"typescript": "^5"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// --- Severity ---
|
|
4
|
+
|
|
5
|
+
export type Severity = "error" | "warning" | "info";
|
|
6
|
+
|
|
7
|
+
// --- Grep Match ---
|
|
8
|
+
|
|
9
|
+
export interface GrepMatch {
|
|
10
|
+
file: string;
|
|
11
|
+
line: number;
|
|
12
|
+
column: number;
|
|
13
|
+
content: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// --- Violation Detail ---
|
|
17
|
+
|
|
18
|
+
export interface ViolationDetail {
|
|
19
|
+
ruleId: string;
|
|
20
|
+
adrId: string;
|
|
21
|
+
message: string;
|
|
22
|
+
file?: string;
|
|
23
|
+
line?: number;
|
|
24
|
+
fix?: string;
|
|
25
|
+
severity: Severity;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// --- Report interface (side-effect based) ---
|
|
29
|
+
|
|
30
|
+
export interface RuleReport {
|
|
31
|
+
violation(
|
|
32
|
+
detail: Omit<ViolationDetail, "ruleId" | "adrId" | "severity">
|
|
33
|
+
): void;
|
|
34
|
+
warning(detail: Omit<ViolationDetail, "ruleId" | "adrId" | "severity">): void;
|
|
35
|
+
info(detail: Omit<ViolationDetail, "ruleId" | "adrId" | "severity">): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// --- Rule Context ---
|
|
39
|
+
|
|
40
|
+
export interface RuleContext {
|
|
41
|
+
projectRoot: string;
|
|
42
|
+
scopedFiles: string[];
|
|
43
|
+
changedFiles: string[];
|
|
44
|
+
glob(pattern: string): Promise<string[]>;
|
|
45
|
+
grep(file: string, pattern: RegExp): Promise<GrepMatch[]>;
|
|
46
|
+
grepFiles(pattern: RegExp, fileGlob: string): Promise<GrepMatch[]>;
|
|
47
|
+
readFile(path: string): Promise<string>;
|
|
48
|
+
readJSON(path: string): Promise<unknown>;
|
|
49
|
+
report: RuleReport;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// --- Rule Config ---
|
|
53
|
+
|
|
54
|
+
export interface RuleConfig {
|
|
55
|
+
description: string;
|
|
56
|
+
severity?: Severity;
|
|
57
|
+
check: (ctx: RuleContext) => Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// --- Rule Set ---
|
|
61
|
+
|
|
62
|
+
export type RuleSet = {
|
|
63
|
+
rules: Record<string, RuleConfig>;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const RuleSetSchema = z.object({
|
|
67
|
+
rules: z.record(
|
|
68
|
+
z.string(),
|
|
69
|
+
z.object({
|
|
70
|
+
description: z.string(),
|
|
71
|
+
severity: z.enum(["error", "warning", "info"]).optional(),
|
|
72
|
+
check: z.custom<(ctx: RuleContext) => Promise<void>>(
|
|
73
|
+
(val) => typeof val === "function",
|
|
74
|
+
"Expected a function"
|
|
75
|
+
),
|
|
76
|
+
})
|
|
77
|
+
),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Define rules in a .rules.ts companion file.
|
|
82
|
+
* Keys become rule IDs, preventing duplicates.
|
|
83
|
+
*/
|
|
84
|
+
export function defineRules(rules: Record<string, RuleConfig>): RuleSet {
|
|
85
|
+
return { rules };
|
|
86
|
+
}
|