agents-md-gen 0.1.1 → 0.3.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 CHANGED
@@ -20,8 +20,22 @@ on immediately.
20
20
  - **Rust** — Cargo, workspaces, clippy
21
21
  - **Go** — go.mod, golangci-lint
22
22
  - **Ruby** — Bundler, RSpec
23
+ - **Java/Kotlin** — Maven, Gradle (with wrapper detection)
23
24
  - CI (GitHub Actions), license, and top-level project structure
24
25
 
26
+ ## GitHub Action
27
+
28
+ Use it directly in CI without installing anything:
29
+
30
+ ```yaml
31
+ - uses: Peshino2012/PD1@claude/busy-newton-5h97oh
32
+ with:
33
+ directory: .
34
+ also-claude-md: 'true'
35
+ ```
36
+
37
+ (pin to a tagged release instead of a branch once one exists)
38
+
25
39
  A repo can match more than one of these (e.g. a Python backend next to a
26
40
  Node frontend) — each detected stack gets its own section.
27
41
 
@@ -33,12 +47,18 @@ npx agents-md-gen ./some/repo # target a different directory
33
47
  npx agents-md-gen --also-claude-md
34
48
  npx agents-md-gen --dry-run # print instead of writing
35
49
  npx agents-md-gen --force # overwrite an existing file
50
+ npx agents-md-gen --check # exit 1 if missing/stale, writes nothing (CI)
36
51
  ```
37
52
 
38
53
  It won't overwrite an existing `AGENTS.md`/`CLAUDE.md` unless you pass
39
54
  `--force` — this is meant to bootstrap a file you then edit by hand, not to
40
55
  clobber one you already wrote.
41
56
 
57
+ `--check` is for CI: it fails the build if `AGENTS.md` doesn't exist or no
58
+ longer matches what the repo would generate, so a stale file (say, a new
59
+ test framework got added but nobody regenerated it) gets caught instead of
60
+ silently rotting. Pair it with the GitHub Action below.
61
+
42
62
  ## Example
43
63
 
44
64
  Real output — this is what `npx agents-md-gen` produces when run against
package/bin/cli.js CHANGED
@@ -11,6 +11,7 @@ function main() {
11
11
  'also-claude-md': { type: 'boolean', default: false },
12
12
  force: { type: 'boolean', short: 'f', default: false },
13
13
  'dry-run': { type: 'boolean', default: false },
14
+ check: { type: 'boolean', default: false },
14
15
  help: { type: 'boolean', short: 'h', default: false },
15
16
  },
16
17
  allowPositionals: true,
@@ -26,6 +27,7 @@ Options:
26
27
  --also-claude-md Also write an identical CLAUDE.md
27
28
  -f, --force Overwrite existing file(s)
28
29
  --dry-run Print to stdout instead of writing
30
+ --check Exit 1 if the file is missing or out of date (CI use, writes nothing)
29
31
  -h, --help Show this help
30
32
  `);
31
33
  return;
@@ -33,6 +35,25 @@ Options:
33
35
 
34
36
  const cwd = positionals[0] ? path.resolve(positionals[0]) : process.cwd();
35
37
 
38
+ if (values.check) {
39
+ const { upToDate, existed, target } = run({
40
+ cwd,
41
+ output: values.output,
42
+ check: true,
43
+ });
44
+ if (upToDate) {
45
+ console.log(`${target} is up to date`);
46
+ return;
47
+ }
48
+ console.error(
49
+ existed
50
+ ? `${target} is out of date - run agents-md-gen --force to update it`
51
+ : `${target} does not exist - run agents-md-gen to create it`
52
+ );
53
+ process.exitCode = 1;
54
+ return;
55
+ }
56
+
36
57
  const { written, skipped } = run({
37
58
  cwd,
38
59
  output: values.output,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-md-gen",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Generate a solid AGENTS.md / CLAUDE.md for any repo by detecting its stack, commands, and structure",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -26,6 +26,9 @@
26
26
  "github-copilot",
27
27
  "openai-codex",
28
28
  "readme-generator",
29
+ "java",
30
+ "maven",
31
+ "gradle",
29
32
  "cli",
30
33
  "codegen",
31
34
  "developer-tools"
package/src/detect.js CHANGED
@@ -165,6 +165,50 @@ function detectGo(root) {
165
165
  };
166
166
  }
167
167
 
168
+ function detectJava(root) {
169
+ const hasGradle = exists(root, 'build.gradle') || exists(root, 'build.gradle.kts');
170
+ const hasMaven = exists(root, 'pom.xml');
171
+ if (!hasGradle && !hasMaven) return null;
172
+
173
+ if (hasGradle) {
174
+ const gradleCmd = exists(root, 'gradlew') ? './gradlew' : 'gradle';
175
+ return {
176
+ ecosystem: 'Java/Kotlin (Gradle)',
177
+ name: null,
178
+ description: null,
179
+ packageManager: 'gradle',
180
+ frameworks: [],
181
+ testFramework: null,
182
+ commands: {
183
+ install: `${gradleCmd} build`,
184
+ build: `${gradleCmd} build`,
185
+ test: `${gradleCmd} test`,
186
+ lint: null,
187
+ dev: null,
188
+ },
189
+ };
190
+ }
191
+
192
+ const pom = readText(root, 'pom.xml') || '';
193
+ const nameMatch = pom.match(/<artifactId>([^<]+)<\/artifactId>/);
194
+ const mvnCmd = exists(root, 'mvnw') ? './mvnw' : 'mvn';
195
+ return {
196
+ ecosystem: 'Java (Maven)',
197
+ name: nameMatch ? nameMatch[1] : null,
198
+ description: null,
199
+ packageManager: 'maven',
200
+ frameworks: [],
201
+ testFramework: null,
202
+ commands: {
203
+ install: `${mvnCmd} install`,
204
+ build: `${mvnCmd} package`,
205
+ test: `${mvnCmd} test`,
206
+ lint: null,
207
+ dev: null,
208
+ },
209
+ };
210
+ }
211
+
168
212
  function detectRuby(root) {
169
213
  if (!exists(root, 'Gemfile')) return null;
170
214
  const hasRspec = exists(root, '.rspec') || exists(root, 'spec');
@@ -185,6 +229,11 @@ function detectRuby(root) {
185
229
  };
186
230
  }
187
231
 
232
+ // Excluded so re-running the generator doesn't see its own prior output as
233
+ // part of the repo's structure - otherwise the file it just wrote would
234
+ // never match a freshly rendered one, breaking `--check`.
235
+ const OWN_OUTPUT_FILES = new Set(['AGENTS.md', 'CLAUDE.md']);
236
+
188
237
  function listStructure(root) {
189
238
  let entries;
190
239
  try {
@@ -195,6 +244,7 @@ function listStructure(root) {
195
244
  return entries
196
245
  .filter((e) => !e.name.startsWith('.') || e.name === '.github')
197
246
  .filter((e) => !IGNORE_DIRS.has(e.name))
247
+ .filter((e) => !OWN_OUTPUT_FILES.has(e.name))
198
248
  .map((e) => (e.isDirectory() ? `${e.name}/` : e.name))
199
249
  .sort();
200
250
  }
@@ -223,7 +273,7 @@ function detectLicense(root) {
223
273
  }
224
274
 
225
275
  function detectProject(root) {
226
- const detectors = [detectNode, detectPython, detectRust, detectGo, detectRuby];
276
+ const detectors = [detectNode, detectPython, detectRust, detectGo, detectRuby, detectJava];
227
277
  const stacks = detectors.map((fn) => fn(root)).filter(Boolean);
228
278
 
229
279
  return {
package/src/index.js CHANGED
@@ -4,7 +4,7 @@ const path = require('path');
4
4
  const { detectProject } = require('./detect');
5
5
  const { renderAgentsMd } = require('./render');
6
6
 
7
- function run({ cwd, output, alsoClaudeMd, force, dryRun }) {
7
+ function run({ cwd, output, alsoClaudeMd, force, dryRun, check }) {
8
8
  const project = detectProject(cwd);
9
9
  const content = renderAgentsMd(project);
10
10
 
@@ -13,6 +13,13 @@ function run({ cwd, output, alsoClaudeMd, force, dryRun }) {
13
13
  return { written: [], skipped: [] };
14
14
  }
15
15
 
16
+ if (check) {
17
+ const fullPath = path.join(cwd, output);
18
+ const existing = fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : null;
19
+ const upToDate = existing === content;
20
+ return { upToDate, existed: existing !== null, target: output };
21
+ }
22
+
16
23
  const targets = [output];
17
24
  if (alsoClaudeMd && !targets.includes('CLAUDE.md')) targets.push('CLAUDE.md');
18
25