compose-agentsmd 1.0.1 → 1.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/README.md +10 -0
- package/agent-ruleset.schema.json +38 -0
- package/dist/compose-agents.js +32 -26
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
This repository contains CLI tooling for composing per-project `AGENTS.md` files from modular rule sets.
|
|
4
4
|
|
|
5
|
+
## Release notes
|
|
6
|
+
|
|
7
|
+
See `CHANGELOG.md` for release notes.
|
|
8
|
+
|
|
5
9
|
It is intended to be used together with shared rule modules such as:
|
|
6
10
|
|
|
7
11
|
- `agent-rules/` (public rule modules)
|
|
@@ -41,6 +45,8 @@ compose-agentsmd
|
|
|
41
45
|
|
|
42
46
|
The tool searches for `agent-ruleset.json` under the given root directory (default: current working directory), and writes output files as specified by each ruleset. If `output` is omitted, it defaults to `AGENTS.md`.
|
|
43
47
|
|
|
48
|
+
The tool prepends a small "Tool Rules" block to every generated `AGENTS.md` so agents know how to regenerate or update rules.
|
|
49
|
+
|
|
44
50
|
### Rules root resolution (important for global installs)
|
|
45
51
|
|
|
46
52
|
When installed globally, the rules directory is usually outside the project. You can point to it in either of the following ways:
|
|
@@ -78,6 +84,10 @@ Rules root resolution precedence is:
|
|
|
78
84
|
- `domains` selects domain folders under `agent-rules/rules/domains`.
|
|
79
85
|
- `rules` is optional and appends additional rule files.
|
|
80
86
|
|
|
87
|
+
### Ruleset schema validation
|
|
88
|
+
|
|
89
|
+
`compose-agentsmd` validates rulesets against `agent-ruleset.schema.json` on every run. If the ruleset does not conform to the schema, the tool exits with a schema error.
|
|
90
|
+
|
|
81
91
|
Optional path overrides:
|
|
82
92
|
|
|
83
93
|
- `rulesRoot`: override `agent-rules/rules`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "Compose AGENTS.md ruleset",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"additionalProperties": false,
|
|
6
|
+
"properties": {
|
|
7
|
+
"output": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"minLength": 1
|
|
10
|
+
},
|
|
11
|
+
"domains": {
|
|
12
|
+
"type": "array",
|
|
13
|
+
"items": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"minLength": 1
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"rules": {
|
|
19
|
+
"type": "array",
|
|
20
|
+
"items": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"minLength": 1
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"rulesRoot": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"minLength": 1
|
|
28
|
+
},
|
|
29
|
+
"globalDir": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"minLength": 1
|
|
32
|
+
},
|
|
33
|
+
"domainsDir": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"minLength": 1
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
package/dist/compose-agents.js
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { Ajv } from "ajv";
|
|
4
5
|
const DEFAULT_RULESET_NAME = "agent-ruleset.json";
|
|
5
6
|
const DEFAULT_RULES_ROOT = "agent-rules/rules";
|
|
6
7
|
const DEFAULT_GLOBAL_DIR = "global";
|
|
7
8
|
const DEFAULT_DOMAINS_DIR = "domains";
|
|
8
9
|
const RULES_ROOT_ENV_VAR = "AGENT_RULES_ROOT";
|
|
9
10
|
const DEFAULT_OUTPUT = "AGENTS.md";
|
|
11
|
+
const RULESET_SCHEMA_PATH = new URL("../agent-ruleset.schema.json", import.meta.url);
|
|
12
|
+
const TOOL_RULES = [
|
|
13
|
+
"# Tool Rules (compose-agentsmd)",
|
|
14
|
+
"- Before starting any work, run `compose-agentsmd` from the project root.",
|
|
15
|
+
"- To update rules, update the source rule modules or ruleset, then rerun `compose-agentsmd`.",
|
|
16
|
+
"- Do not edit `AGENTS.md` directly; update the source rules and regenerate."
|
|
17
|
+
].join("\n");
|
|
10
18
|
const DEFAULT_IGNORE_DIRS = new Set([
|
|
11
19
|
".git",
|
|
12
20
|
"agent-rules",
|
|
@@ -81,6 +89,20 @@ const parseArgs = (argv) => {
|
|
|
81
89
|
const normalizeTrailingWhitespace = (content) => content.replace(/\s+$/u, "");
|
|
82
90
|
const normalizePath = (filePath) => filePath.replace(/\\/g, "/");
|
|
83
91
|
const isNonEmptyString = (value) => typeof value === "string" && value.trim() !== "";
|
|
92
|
+
const rulesetSchema = JSON.parse(fs.readFileSync(RULESET_SCHEMA_PATH, "utf8"));
|
|
93
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
94
|
+
const validateRulesetSchema = ajv.compile(rulesetSchema);
|
|
95
|
+
const formatSchemaErrors = (errors) => {
|
|
96
|
+
if (!errors || errors.length === 0) {
|
|
97
|
+
return "Unknown schema validation error";
|
|
98
|
+
}
|
|
99
|
+
return errors
|
|
100
|
+
.map((error) => {
|
|
101
|
+
const pathLabel = error.instancePath ? error.instancePath : "(root)";
|
|
102
|
+
return `${pathLabel} ${error.message ?? "is invalid"}`;
|
|
103
|
+
})
|
|
104
|
+
.join("; ");
|
|
105
|
+
};
|
|
84
106
|
const resolveFrom = (baseDir, targetPath) => {
|
|
85
107
|
if (path.isAbsolute(targetPath)) {
|
|
86
108
|
return targetPath;
|
|
@@ -107,33 +129,16 @@ const readJsonFile = (filePath) => {
|
|
|
107
129
|
};
|
|
108
130
|
const readProjectRuleset = (rulesetPath) => {
|
|
109
131
|
const parsed = readJsonFile(rulesetPath);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
throw new Error(`Invalid ruleset output in ${rulesetPath}`);
|
|
115
|
-
}
|
|
116
|
-
if (parsed.domains !== undefined) {
|
|
117
|
-
if (!Array.isArray(parsed.domains)) {
|
|
118
|
-
throw new Error(`"domains" must be an array in ${rulesetPath}`);
|
|
119
|
-
}
|
|
120
|
-
for (const domain of parsed.domains) {
|
|
121
|
-
if (!isNonEmptyString(domain)) {
|
|
122
|
-
throw new Error(`"domains" entries must be non-empty strings in ${rulesetPath}`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
132
|
+
const isValid = validateRulesetSchema(parsed);
|
|
133
|
+
if (!isValid) {
|
|
134
|
+
const message = formatSchemaErrors(validateRulesetSchema.errors);
|
|
135
|
+
throw new Error(`Invalid ruleset schema in ${rulesetPath}: ${message}`);
|
|
125
136
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
for (const rule of parsed.rules) {
|
|
131
|
-
if (!isNonEmptyString(rule)) {
|
|
132
|
-
throw new Error(`"rules" entries must be non-empty strings in ${rulesetPath}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
137
|
+
const ruleset = parsed;
|
|
138
|
+
if (ruleset.output === undefined) {
|
|
139
|
+
ruleset.output = DEFAULT_OUTPUT;
|
|
135
140
|
}
|
|
136
|
-
return
|
|
141
|
+
return ruleset;
|
|
137
142
|
};
|
|
138
143
|
const resolveRulesRoot = (rulesetDir, projectRuleset, options) => {
|
|
139
144
|
if (isNonEmptyString(options.cliRulesRoot)) {
|
|
@@ -222,7 +227,8 @@ const composeRuleset = (rulesetPath, rootDir, options) => {
|
|
|
222
227
|
addRulePaths(directRulePaths, resolvedRules, seenRules);
|
|
223
228
|
const parts = resolvedRules.map((rulePath) => normalizeTrailingWhitespace(fs.readFileSync(rulePath, "utf8")));
|
|
224
229
|
const lintHeader = "<!-- markdownlint-disable MD025 -->";
|
|
225
|
-
const
|
|
230
|
+
const toolRules = normalizeTrailingWhitespace(TOOL_RULES);
|
|
231
|
+
const output = `${lintHeader}\n${[toolRules, ...parts].join("\n\n")}\n`;
|
|
226
232
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
227
233
|
fs.writeFileSync(outputPath, output, "utf8");
|
|
228
234
|
return normalizePath(path.relative(rootDir, outputPath));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compose-agentsmd",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "CLI tools for composing per-project AGENTS.md files from modular rule sets",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
|
+
"agent-ruleset.schema.json",
|
|
27
28
|
"tools",
|
|
28
29
|
"README.md",
|
|
29
30
|
"LICENSE"
|
|
@@ -45,5 +46,8 @@
|
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@types/node": "^25.0.10",
|
|
47
48
|
"typescript": "^5.7.3"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"ajv": "^8.17.1"
|
|
48
52
|
}
|
|
49
53
|
}
|