compose-agentsmd 3.2.3 → 3.2.5

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 CHANGED
@@ -134,4 +134,11 @@ npm install
134
134
  npm run lint
135
135
  npm run build
136
136
  npm test
137
- ```
137
+ ```
138
+
139
+ ## Overview
140
+ This repository contains the compose-agentsmd project.
141
+
142
+ ## Requirements and Configuration
143
+ - No required environment variables are documented.
144
+
@@ -16,6 +16,13 @@ const RULESET_SCHEMA_PATH = new URL("../agent-ruleset.schema.json", import.meta.
16
16
  const PACKAGE_JSON_PATH = new URL("../package.json", import.meta.url);
17
17
  const TOOL_RULES_PATH = new URL("../tools/tool-rules.md", import.meta.url);
18
18
  const USAGE_PATH = new URL("../tools/usage.txt", import.meta.url);
19
+ const readValueArg = (remaining, index, flag) => {
20
+ const value = remaining[index + 1];
21
+ if (!value) {
22
+ throw new Error(`Missing value for ${flag}`);
23
+ }
24
+ return value;
25
+ };
19
26
  const parseArgs = (argv) => {
20
27
  const args = {};
21
28
  const knownCommands = new Set(["edit-rules", "apply-rules", "init"]);
@@ -37,29 +44,28 @@ const parseArgs = (argv) => {
37
44
  args.verbose = true;
38
45
  continue;
39
46
  }
47
+ if (arg === "--quiet" || arg === "-q") {
48
+ args.quiet = true;
49
+ continue;
50
+ }
51
+ if (arg === "--json") {
52
+ args.json = true;
53
+ continue;
54
+ }
40
55
  if (arg === "--root") {
41
- const value = remaining[i + 1];
42
- if (!value) {
43
- throw new Error("Missing value for --root");
44
- }
56
+ const value = readValueArg(remaining, i, "--root");
45
57
  args.root = value;
46
58
  i += 1;
47
59
  continue;
48
60
  }
49
61
  if (arg === "--ruleset") {
50
- const value = remaining[i + 1];
51
- if (!value) {
52
- throw new Error("Missing value for --ruleset");
53
- }
62
+ const value = readValueArg(remaining, i, "--ruleset");
54
63
  args.ruleset = value;
55
64
  i += 1;
56
65
  continue;
57
66
  }
58
67
  if (arg === "--ruleset-name") {
59
- const value = remaining[i + 1];
60
- if (!value) {
61
- throw new Error("Missing value for --ruleset-name");
62
- }
68
+ const value = readValueArg(remaining, i, "--ruleset-name");
63
69
  args.rulesetName = value;
64
70
  i += 1;
65
71
  continue;
@@ -73,19 +79,13 @@ const parseArgs = (argv) => {
73
79
  continue;
74
80
  }
75
81
  if (arg === "--source") {
76
- const value = remaining[i + 1];
77
- if (!value) {
78
- throw new Error("Missing value for --source");
79
- }
82
+ const value = readValueArg(remaining, i, "--source");
80
83
  args.source = value;
81
84
  i += 1;
82
85
  continue;
83
86
  }
84
87
  if (arg === "--domains") {
85
- const value = remaining[i + 1];
86
- if (!value) {
87
- throw new Error("Missing value for --domains");
88
- }
88
+ const value = readValueArg(remaining, i, "--domains");
89
89
  args.domains = [...(args.domains ?? []), ...value.split(",").map((entry) => entry.trim())];
90
90
  i += 1;
91
91
  continue;
@@ -95,10 +95,7 @@ const parseArgs = (argv) => {
95
95
  continue;
96
96
  }
97
97
  if (arg === "--extra") {
98
- const value = remaining[i + 1];
99
- if (!value) {
100
- throw new Error("Missing value for --extra");
101
- }
98
+ const value = readValueArg(remaining, i, "--extra");
102
99
  args.extra = [...(args.extra ?? []), ...value.split(",").map((entry) => entry.trim())];
103
100
  i += 1;
104
101
  continue;
@@ -108,10 +105,7 @@ const parseArgs = (argv) => {
108
105
  continue;
109
106
  }
110
107
  if (arg === "--output") {
111
- const value = remaining[i + 1];
112
- if (!value) {
113
- throw new Error("Missing value for --output");
114
- }
108
+ const value = readValueArg(remaining, i, "--output");
115
109
  args.output = value;
116
110
  i += 1;
117
111
  continue;
@@ -550,8 +544,10 @@ const composeRuleset = (rulesetPath, rootDir, options) => {
550
544
  const lintHeader = "<!-- markdownlint-disable MD025 -->";
551
545
  const toolRules = normalizeTrailingWhitespace(TOOL_RULES);
552
546
  const output = `${lintHeader}\n${[toolRules, ...parts].join("\n\n")}\n`;
553
- fs.mkdirSync(path.dirname(outputPath), { recursive: true });
554
- fs.writeFileSync(outputPath, output, "utf8");
547
+ if (!options.dryRun) {
548
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
549
+ fs.writeFileSync(outputPath, output, "utf8");
550
+ }
555
551
  return normalizePath(path.relative(rootDir, outputPath));
556
552
  };
557
553
  const LOCAL_RULES_TEMPLATE = "# Local Rules\n\n- Add project-specific instructions here.\n";
@@ -655,9 +651,22 @@ const initProject = async (args, rootDir, rulesetName) => {
655
651
  plan.push({ action: "create", path: outputPath });
656
652
  }
657
653
  }
658
- process.stdout.write(formatPlan(plan, rootDir));
654
+ if (!args.quiet && !args.json) {
655
+ process.stdout.write(formatPlan(plan, rootDir));
656
+ }
659
657
  if (args.dryRun) {
660
- process.stdout.write("Dry run: no changes made.\n");
658
+ if (args.json) {
659
+ process.stdout.write(JSON.stringify({
660
+ dryRun: true,
661
+ plan: plan.map((item) => ({
662
+ action: item.action,
663
+ path: normalizePath(path.relative(rootDir, item.path))
664
+ }))
665
+ }, null, 2) + "\n");
666
+ }
667
+ else if (!args.quiet) {
668
+ process.stdout.write("Dry run: no changes made.\n");
669
+ }
661
670
  return;
662
671
  }
663
672
  await confirmInit(args);
@@ -667,15 +676,28 @@ const initProject = async (args, rootDir, rulesetName) => {
667
676
  fs.mkdirSync(path.dirname(extraPath), { recursive: true });
668
677
  fs.writeFileSync(extraPath, LOCAL_RULES_TEMPLATE, "utf8");
669
678
  }
670
- process.stdout.write(`Initialized ruleset:\n- ${normalizePath(path.relative(rootDir, rulesetPath))}\n`);
671
- if (extraToWrite.length > 0) {
672
- process.stdout.write(`Initialized local rules:\n${extraToWrite
673
- .map((filePath) => `- ${normalizePath(path.relative(rootDir, filePath))}`)
674
- .join("\n")}\n`);
675
- }
679
+ let composedOutput;
676
680
  if (args.compose) {
677
- const output = composeRuleset(rulesetPath, rootDir, { refresh: args.refresh ?? false });
678
- process.stdout.write(`Composed AGENTS.md:\n- ${output}\n`);
681
+ composedOutput = composeRuleset(rulesetPath, rootDir, { refresh: args.refresh ?? false });
682
+ }
683
+ if (args.json) {
684
+ process.stdout.write(JSON.stringify({
685
+ initialized: [normalizePath(path.relative(rootDir, rulesetPath))],
686
+ localRules: extraToWrite.map((filePath) => normalizePath(path.relative(rootDir, filePath))),
687
+ composed: composedOutput ? [composedOutput] : [],
688
+ dryRun: false
689
+ }, null, 2) + "\n");
690
+ }
691
+ else if (!args.quiet) {
692
+ process.stdout.write(`Initialized ruleset:\n- ${normalizePath(path.relative(rootDir, rulesetPath))}\n`);
693
+ if (extraToWrite.length > 0) {
694
+ process.stdout.write(`Initialized local rules:\n${extraToWrite
695
+ .map((filePath) => `- ${normalizePath(path.relative(rootDir, filePath))}`)
696
+ .join("\n")}\n`);
697
+ }
698
+ if (composedOutput) {
699
+ process.stdout.write(`Composed AGENTS.md:\n- ${composedOutput}\n`);
700
+ }
679
701
  }
680
702
  };
681
703
  const getRulesetFiles = (rootDir, specificRuleset, rulesetName) => {
@@ -745,8 +767,13 @@ const main = async () => {
745
767
  const rulesetDir = path.dirname(rulesetPath);
746
768
  const ruleset = readProjectRuleset(rulesetPath);
747
769
  applyRulesFromWorkspace(rulesetDir, ruleset.source);
748
- const output = composeRuleset(rulesetPath, rootDir, { refresh: true });
749
- process.stdout.write(`Composed AGENTS.md:\n- ${output}\n`);
770
+ const output = composeRuleset(rulesetPath, rootDir, { refresh: true, dryRun: args.dryRun });
771
+ if (args.json) {
772
+ process.stdout.write(JSON.stringify({ composed: [output], dryRun: !!args.dryRun }, null, 2) + "\n");
773
+ }
774
+ else if (!args.quiet) {
775
+ process.stdout.write(`Composed AGENTS.md:\n- ${output}\n`);
776
+ }
750
777
  return;
751
778
  }
752
779
  if (rulesetFiles.length === 0) {
@@ -755,8 +782,13 @@ const main = async () => {
755
782
  }
756
783
  const outputs = rulesetFiles
757
784
  .sort()
758
- .map((rulesetPath) => composeRuleset(rulesetPath, rootDir, { refresh: args.refresh }));
759
- process.stdout.write(`Composed AGENTS.md:\n${outputs.map((file) => `- ${file}`).join("\n")}\n`);
785
+ .map((rulesetPath) => composeRuleset(rulesetPath, rootDir, { refresh: args.refresh, dryRun: args.dryRun }));
786
+ if (args.json) {
787
+ process.stdout.write(JSON.stringify({ composed: outputs, dryRun: !!args.dryRun }, null, 2) + "\n");
788
+ }
789
+ else if (!args.quiet) {
790
+ process.stdout.write(`Composed AGENTS.md:\n${outputs.map((file) => `- ${file}`).join("\n")}\n`);
791
+ }
760
792
  };
761
793
  const run = async () => {
762
794
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "compose-agentsmd",
3
- "version": "3.2.3",
3
+ "version": "3.2.5",
4
4
  "description": "CLI tools for composing per-project AGENTS.md files from modular rule sets",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,7 +1,11 @@
1
1
  # Tool Rules (compose-agentsmd)
2
2
  - Before starting any work, run `compose-agentsmd` from the project root.
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
+ - If `compose-agentsmd` is not available, install it via npm: `npm install -g compose-agentsmd`.
3
5
  - To update shared rules, run `compose-agentsmd edit-rules`, edit the workspace rules, then run `compose-agentsmd apply-rules`.
4
6
  - Do not edit `AGENTS.md` directly; update the source rules and regenerate.
7
+ - `tools/tool-rules.md` is the shared rule source for all repositories that use compose-agentsmd.
8
+ - Before applying any rule updates, present the planned changes first (prefer a colorized diff-style preview), ask for explicit approval, then make the edits.
5
9
  - These tool rules live in tools/tool-rules.md in the compose-agentsmd repository; do not duplicate them in global rule modules.
6
10
  - When updating rules, include a colorized diff-style summary in the final response. Use `git diff --stat` first, then include the raw ANSI-colored output of `git diff --color=always` (no sanitizing or reformatting), and limit the output to the rule files that changed.
7
11
  - Also provide a short, copy-pasteable command the user can run to view the diff in the same format. Use absolute paths so it works regardless of the current working directory, and scope it to the changed rule files.
package/tools/usage.txt CHANGED
@@ -1,9 +1,11 @@
1
- Usage: compose-agentsmd [edit-rules|apply-rules|init] [--root <path>] [--ruleset <path>] [--ruleset-name <name>] [--source <source>] [--domains <list>] [--extra <list>] [--output <file>] [--no-domains] [--no-extra] [--no-global] [--compose] [--dry-run] [--yes] [--force] [--refresh] [--clear-cache] [--version|-V] [--verbose|-v] [--help|-h]
1
+ Usage: compose-agentsmd [edit-rules|apply-rules|init] [--root <path>] [--ruleset <path>] [--ruleset-name <name>] [--source <source>] [--domains <list>] [--extra <list>] [--output <file>] [--no-domains] [--no-extra] [--no-global] [--compose] [--dry-run] [--yes] [--force] [--refresh] [--clear-cache] [--version|-V] [--verbose|-v] [--quiet|-q] [--json] [--help|-h]
2
2
 
3
3
  Options:
4
4
  --help, -h Show help and exit
5
5
  --version, -V Show version and exit
6
6
  --verbose, -v Show verbose diagnostics
7
+ --quiet, -q Suppress non-error human-readable output (JSON output is not suppressed)
8
+ --json Output machine-readable JSON (takes precedence over --quiet)
7
9
  --root <path> Project root directory (default: current working directory)
8
10
  --ruleset <path> Only compose a single ruleset file
9
11
  --ruleset-name <name> Ruleset filename in the project root (default: agent-ruleset.json)