compose-agentsmd 3.2.5 → 3.2.7
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 +3 -1
- package/dist/compose-agents.js +70 -11
- package/package.json +3 -2
- package/tools/tool-rules.md +1 -0
package/README.md
CHANGED
|
@@ -35,6 +35,8 @@ The tool reads `agent-ruleset.json` from the given root directory (default: curr
|
|
|
35
35
|
The tool prepends a small "Tool Rules" block to every generated `AGENTS.md` so agents know how to regenerate or update rules.
|
|
36
36
|
Each composed rule section is also prefixed with the source file path that produced it.
|
|
37
37
|
|
|
38
|
+
When the output file is `AGENTS.md`, the CLI also prints a unified diff for `AGENTS.md` when it changes (and prints `AGENTS.md unchanged.` when it does not). This works even when the project is not under git. `--quiet` and `--json` suppress this output.
|
|
39
|
+
|
|
38
40
|
## Setup (init)
|
|
39
41
|
|
|
40
42
|
For a project that does not have a ruleset yet, bootstrap one with `init`:
|
|
@@ -68,7 +70,7 @@ compose-agentsmd edit-rules
|
|
|
68
70
|
compose-agentsmd apply-rules
|
|
69
71
|
```
|
|
70
72
|
|
|
71
|
-
`edit-rules` clones the GitHub source into the workspace (or reuses it). `apply-rules` pushes the workspace (if clean) and regenerates `AGENTS.md` by refreshing the cache. If your `source` is a local path, `edit-rules`
|
|
73
|
+
`edit-rules` clones the GitHub source into the workspace (or reuses it), then prints the workspace path, rules directory, and next steps. `apply-rules` pushes the workspace (if clean) and regenerates `AGENTS.md` by refreshing the cache. If your `source` is a local path, `edit-rules` points to the local workspace and `apply-rules` skips the push.
|
|
72
74
|
|
|
73
75
|
## Project ruleset format
|
|
74
76
|
|
package/dist/compose-agents.js
CHANGED
|
@@ -5,6 +5,7 @@ import os from "node:os";
|
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
6
|
import readline from "node:readline";
|
|
7
7
|
import { Ajv } from "ajv";
|
|
8
|
+
import { createTwoFilesPatch } from "diff";
|
|
8
9
|
const DEFAULT_RULESET_NAME = "agent-ruleset.json";
|
|
9
10
|
const DEFAULT_OUTPUT = "AGENTS.md";
|
|
10
11
|
const DEFAULT_CACHE_ROOT = path.join(os.homedir(), ".agentsmd", "cache");
|
|
@@ -520,6 +521,7 @@ const composeRuleset = (rulesetPath, rootDir, options) => {
|
|
|
520
521
|
const projectRuleset = readProjectRuleset(rulesetPath);
|
|
521
522
|
const outputFileName = projectRuleset.output ?? DEFAULT_OUTPUT;
|
|
522
523
|
const outputPath = resolveFrom(rulesetDir, outputFileName);
|
|
524
|
+
const composedOutputPath = normalizePath(path.relative(rootDir, outputPath));
|
|
523
525
|
const { rulesRoot, resolvedRef } = resolveRulesRoot(rulesetDir, projectRuleset.source, options.refresh ?? false);
|
|
524
526
|
const globalRoot = path.join(rulesRoot, "global");
|
|
525
527
|
const domainsRoot = path.join(rulesRoot, "domains");
|
|
@@ -544,11 +546,42 @@ const composeRuleset = (rulesetPath, rootDir, options) => {
|
|
|
544
546
|
const lintHeader = "<!-- markdownlint-disable MD025 -->";
|
|
545
547
|
const toolRules = normalizeTrailingWhitespace(TOOL_RULES);
|
|
546
548
|
const output = `${lintHeader}\n${[toolRules, ...parts].join("\n\n")}\n`;
|
|
549
|
+
let agentsMdDiff;
|
|
550
|
+
if (options.emitAgentsMdDiff && path.basename(outputPath) === DEFAULT_OUTPUT) {
|
|
551
|
+
const before = fs.existsSync(outputPath) ? fs.readFileSync(outputPath, "utf8") : "";
|
|
552
|
+
if (before === output) {
|
|
553
|
+
agentsMdDiff = { status: "unchanged" };
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
agentsMdDiff = {
|
|
557
|
+
status: "updated",
|
|
558
|
+
patch: createTwoFilesPatch(`a/${composedOutputPath}`, `b/${composedOutputPath}`, before, output, "", "", { context: 3 })
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
}
|
|
547
562
|
if (!options.dryRun) {
|
|
548
563
|
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
549
564
|
fs.writeFileSync(outputPath, output, "utf8");
|
|
550
565
|
}
|
|
551
|
-
return
|
|
566
|
+
return { output: composedOutputPath, agentsMdDiff };
|
|
567
|
+
};
|
|
568
|
+
const printAgentsMdDiffIfPresent = (result) => {
|
|
569
|
+
if (!result.agentsMdDiff) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (result.agentsMdDiff.status === "unchanged") {
|
|
573
|
+
process.stdout.write("AGENTS.md unchanged.\n");
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
process.stdout.write("AGENTS.md updated. ACTION (agent): refresh rule recognition from the diff below.\n");
|
|
577
|
+
process.stdout.write("--- BEGIN DIFF ---\n");
|
|
578
|
+
if (result.agentsMdDiff.patch) {
|
|
579
|
+
process.stdout.write(result.agentsMdDiff.patch);
|
|
580
|
+
if (!result.agentsMdDiff.patch.endsWith("\n")) {
|
|
581
|
+
process.stdout.write("\n");
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
process.stdout.write("--- END DIFF ---\n");
|
|
552
585
|
};
|
|
553
586
|
const LOCAL_RULES_TEMPLATE = "# Local Rules\n\n- Add project-specific instructions here.\n";
|
|
554
587
|
const buildInitRuleset = (args) => {
|
|
@@ -678,13 +711,16 @@ const initProject = async (args, rootDir, rulesetName) => {
|
|
|
678
711
|
}
|
|
679
712
|
let composedOutput;
|
|
680
713
|
if (args.compose) {
|
|
681
|
-
composedOutput = composeRuleset(rulesetPath, rootDir, {
|
|
714
|
+
composedOutput = composeRuleset(rulesetPath, rootDir, {
|
|
715
|
+
refresh: args.refresh ?? false,
|
|
716
|
+
emitAgentsMdDiff: !args.quiet && !args.json
|
|
717
|
+
});
|
|
682
718
|
}
|
|
683
719
|
if (args.json) {
|
|
684
720
|
process.stdout.write(JSON.stringify({
|
|
685
721
|
initialized: [normalizePath(path.relative(rootDir, rulesetPath))],
|
|
686
722
|
localRules: extraToWrite.map((filePath) => normalizePath(path.relative(rootDir, filePath))),
|
|
687
|
-
composed: composedOutput ? [composedOutput] : [],
|
|
723
|
+
composed: composedOutput ? [composedOutput.output] : [],
|
|
688
724
|
dryRun: false
|
|
689
725
|
}, null, 2) + "\n");
|
|
690
726
|
}
|
|
@@ -696,7 +732,8 @@ const initProject = async (args, rootDir, rulesetName) => {
|
|
|
696
732
|
.join("\n")}\n`);
|
|
697
733
|
}
|
|
698
734
|
if (composedOutput) {
|
|
699
|
-
process.stdout.write(`Composed AGENTS.md:\n- ${composedOutput}\n`);
|
|
735
|
+
process.stdout.write(`Composed AGENTS.md:\n- ${composedOutput.output}\n`);
|
|
736
|
+
printAgentsMdDiffIfPresent(composedOutput);
|
|
700
737
|
}
|
|
701
738
|
}
|
|
702
739
|
};
|
|
@@ -755,7 +792,17 @@ const main = async () => {
|
|
|
755
792
|
if (ruleset.source.startsWith("github:")) {
|
|
756
793
|
workspaceRoot = ensureWorkspaceForGithubSource(ruleset.source);
|
|
757
794
|
}
|
|
758
|
-
|
|
795
|
+
const rulesDirectory = ruleset.source.startsWith("github:")
|
|
796
|
+
? path.join(workspaceRoot, "rules")
|
|
797
|
+
: resolveLocalRulesRoot(rulesetDir, ruleset.source);
|
|
798
|
+
process.stdout.write([
|
|
799
|
+
`Rules workspace: ${workspaceRoot}`,
|
|
800
|
+
`Rules directory: ${rulesDirectory}`,
|
|
801
|
+
"Next steps:",
|
|
802
|
+
`- Edit rule files under: ${rulesDirectory}`,
|
|
803
|
+
"- If this source is GitHub, commit and push the workspace changes before apply-rules.",
|
|
804
|
+
"- Run compose-agentsmd apply-rules from your project root to apply updates and regenerate AGENTS.md."
|
|
805
|
+
].join("\n") + "\n");
|
|
759
806
|
return;
|
|
760
807
|
}
|
|
761
808
|
if (command === "init") {
|
|
@@ -767,12 +814,17 @@ const main = async () => {
|
|
|
767
814
|
const rulesetDir = path.dirname(rulesetPath);
|
|
768
815
|
const ruleset = readProjectRuleset(rulesetPath);
|
|
769
816
|
applyRulesFromWorkspace(rulesetDir, ruleset.source);
|
|
770
|
-
const output = composeRuleset(rulesetPath, rootDir, {
|
|
817
|
+
const output = composeRuleset(rulesetPath, rootDir, {
|
|
818
|
+
refresh: true,
|
|
819
|
+
dryRun: args.dryRun,
|
|
820
|
+
emitAgentsMdDiff: !args.quiet && !args.json
|
|
821
|
+
});
|
|
771
822
|
if (args.json) {
|
|
772
|
-
process.stdout.write(JSON.stringify({ composed: [output], dryRun: !!args.dryRun }, null, 2) + "\n");
|
|
823
|
+
process.stdout.write(JSON.stringify({ composed: [output.output], dryRun: !!args.dryRun }, null, 2) + "\n");
|
|
773
824
|
}
|
|
774
825
|
else if (!args.quiet) {
|
|
775
|
-
process.stdout.write(`Composed AGENTS.md:\n- ${output}\n`);
|
|
826
|
+
process.stdout.write(`Composed AGENTS.md:\n- ${output.output}\n`);
|
|
827
|
+
printAgentsMdDiffIfPresent(output);
|
|
776
828
|
}
|
|
777
829
|
return;
|
|
778
830
|
}
|
|
@@ -782,12 +834,19 @@ const main = async () => {
|
|
|
782
834
|
}
|
|
783
835
|
const outputs = rulesetFiles
|
|
784
836
|
.sort()
|
|
785
|
-
.map((rulesetPath) => composeRuleset(rulesetPath, rootDir, {
|
|
837
|
+
.map((rulesetPath) => composeRuleset(rulesetPath, rootDir, {
|
|
838
|
+
refresh: args.refresh,
|
|
839
|
+
dryRun: args.dryRun,
|
|
840
|
+
emitAgentsMdDiff: !args.quiet && !args.json
|
|
841
|
+
}));
|
|
786
842
|
if (args.json) {
|
|
787
|
-
process.stdout.write(JSON.stringify({ composed: outputs, dryRun: !!args.dryRun }, null, 2) + "\n");
|
|
843
|
+
process.stdout.write(JSON.stringify({ composed: outputs.map((result) => result.output), dryRun: !!args.dryRun }, null, 2) + "\n");
|
|
788
844
|
}
|
|
789
845
|
else if (!args.quiet) {
|
|
790
|
-
process.stdout.write(`Composed AGENTS.md:\n${outputs.map((
|
|
846
|
+
process.stdout.write(`Composed AGENTS.md:\n${outputs.map((result) => `- ${result.output}`).join("\n")}\n`);
|
|
847
|
+
for (const result of outputs) {
|
|
848
|
+
printAgentsMdDiffIfPresent(result);
|
|
849
|
+
}
|
|
791
850
|
}
|
|
792
851
|
};
|
|
793
852
|
const run = async () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compose-agentsmd",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.7",
|
|
4
4
|
"description": "CLI tools for composing per-project AGENTS.md files from modular rule sets",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"typescript": "^5.7.3"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"ajv": "^8.17.1"
|
|
51
|
+
"ajv": "^8.17.1",
|
|
52
|
+
"diff": "^8.0.3"
|
|
52
53
|
}
|
|
53
54
|
}
|
package/tools/tool-rules.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
- `compose-agentsmd` intentionally regenerates `AGENTS.md`; any resulting `AGENTS.md` diff is expected and must not be treated as an unexpected external change.
|
|
4
4
|
- If `compose-agentsmd` is not available, install it via npm: `npm install -g compose-agentsmd`.
|
|
5
5
|
- To update shared rules, run `compose-agentsmd edit-rules`, edit the workspace rules, then run `compose-agentsmd apply-rules`.
|
|
6
|
+
- `compose-agentsmd apply-rules` pushes the rules workspace when `source` is GitHub (if the workspace is clean), then regenerates `AGENTS.md` with refreshed rules.
|
|
6
7
|
- Do not edit `AGENTS.md` directly; update the source rules and regenerate.
|
|
7
8
|
- `tools/tool-rules.md` is the shared rule source for all repositories that use compose-agentsmd.
|
|
8
9
|
- Before applying any rule updates, present the planned changes first (prefer a colorized diff-style preview), ask for explicit approval, then make the edits.
|