agents-md-gen 0.11.0 → 0.13.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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.0
4
+
5
+ - **Multi-project repos**: when the root has no recognizable stack and isn't
6
+ a JS workspace monorepo, each immediate subdirectory is scanned. Every
7
+ stack found gets its own section, e.g. `## Go (\`api/\`)` with "Run these
8
+ commands from `api/`." Previously these repos only got "Couldn't
9
+ auto-detect a specific language/framework".
10
+
11
+ ## 0.12.0
12
+
13
+ - New **Typecheck** and **Format** commands in the generated file — what an
14
+ agent should run before committing. Only emitted with a clear signal:
15
+ package.json `typecheck`/`type-check`/`tsc` and `format`/`fmt` scripts;
16
+ ruff or black, mypy or pyright config (Python); `cargo check`/`cargo fmt`
17
+ (Rust); `gofmt` (Go); `deno fmt` and deno tasks; `dart format`;
18
+ `mix format`.
19
+ - Fix: the Python lint command lacked the `poetry run`/`uv run`/`pipenv run`
20
+ prefix the other commands had, so it failed when ruff/flake8 only lived in
21
+ the project's virtualenv.
22
+ - README: pin a version when using `--check` in CI.
23
+
3
24
  ## 0.11.0
4
25
 
5
26
  - Fix: Bun projects using the text `bun.lock` (the default since Bun 1.2)
package/README.md CHANGED
@@ -77,6 +77,9 @@ docker run --rm -v "$PWD":/workdir ghcr.io/peshino2012/agents-md-gen
77
77
  - **Julia** — `Project.toml`, Pkg test detection
78
78
  - **Monorepos** — Turborepo, Nx, Lerna, Rush, npm/yarn/pnpm workspaces,
79
79
  listing each detected sub-package
80
+ - **Multi-project repos** — nothing at the root (e.g. `api/` in Go next to
81
+ `web/` in Node)? Each immediate subdirectory is scanned and gets its own
82
+ section saying where to run its commands
80
83
  - CI (GitHub Actions), license, and top-level project structure
81
84
 
82
85
  ## GitHub Action
@@ -124,6 +127,11 @@ longer matches what the repo would generate, so a stale file (say, a new
124
127
  test framework got added but nobody regenerated it) gets caught instead of
125
128
  silently rotting. Pair it with the GitHub Action above.
126
129
 
130
+ New releases detect more things, which changes the generated file — so with
131
+ `--check`, pin the version you generated with (`npx agents-md-gen@0.12.0
132
+ --check`, or the action's `version` input) and bump it on purpose, rather
133
+ than letting `@latest` fail CI the day a release lands.
134
+
127
135
  ## Example
128
136
 
129
137
  Real output — this is what `npx agents-md-gen` produces when run against
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-md-gen",
3
- "version": "0.11.0",
3
+ "version": "0.13.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
  "homepage": "https://github.com/Peshino2012/homebrew-tap#readme",
package/src/detect.js CHANGED
@@ -84,6 +84,8 @@ function detectNode(root) {
84
84
  test: run('test'),
85
85
  lint: run('lint'),
86
86
  dev: run('dev') || run('start'),
87
+ typecheck: run('typecheck') || run('type-check') || run('tsc'),
88
+ format: run('format') || run('fmt'),
87
89
  };
88
90
 
89
91
  return {
@@ -121,6 +123,8 @@ function detectDeno(root) {
121
123
  test: task('test') || 'deno test',
122
124
  lint: task('lint') || 'deno lint',
123
125
  dev: task('dev') || task('start'),
126
+ typecheck: task('check') || task('typecheck'),
127
+ format: task('fmt') || task('format') || 'deno fmt',
124
128
  },
125
129
  };
126
130
  }
@@ -162,6 +166,16 @@ function detectPython(root) {
162
166
 
163
167
  const run = { poetry: 'poetry run ', uv: 'uv run ', pipenv: 'pipenv run ' }[packageManager] || '';
164
168
 
169
+ const usesBlack = /^\[tool\.black\]/m.test(pyproject) || hasPythonDep(depsText, 'black');
170
+ const usesMypy = /^\[tool\.mypy\]/m.test(pyproject) || exists(root, 'mypy.ini') || hasPythonDep(depsText, 'mypy');
171
+ const usesPyright = /^\[tool\.pyright\]/m.test(pyproject) || exists(root, 'pyrightconfig.json');
172
+ let formatCmd = null;
173
+ if (usesRuff) formatCmd = `${run}ruff format .`;
174
+ else if (usesBlack) formatCmd = `${run}black .`;
175
+ let typecheckCmd = null;
176
+ if (usesMypy) typecheckCmd = `${run}mypy .`;
177
+ else if (usesPyright) typecheckCmd = `${run}pyright`;
178
+
165
179
  let testCmd = null;
166
180
  if (usesPytest) testCmd = `${run}pytest`;
167
181
  else if (hasManagePy) testCmd = `${run}python manage.py test`;
@@ -177,8 +191,10 @@ function detectPython(root) {
177
191
  install: installCmd,
178
192
  build: null,
179
193
  test: testCmd,
180
- lint: usesRuff ? 'ruff check .' : (exists(root, '.flake8') ? 'flake8' : null),
194
+ lint: usesRuff ? `${run}ruff check .` : (exists(root, '.flake8') ? `${run}flake8` : null),
181
195
  dev: hasManagePy ? `${run}python manage.py runserver` : null,
196
+ typecheck: typecheckCmd,
197
+ format: formatCmd,
182
198
  },
183
199
  };
184
200
  }
@@ -215,6 +231,8 @@ function detectRust(root) {
215
231
  test: 'cargo test',
216
232
  lint: 'cargo clippy',
217
233
  dev: null,
234
+ typecheck: 'cargo check',
235
+ format: 'cargo fmt',
218
236
  },
219
237
  };
220
238
  }
@@ -242,6 +260,7 @@ function detectGo(root) {
242
260
  test: 'go test ./...',
243
261
  lint: (exists(root, '.golangci.yml') || exists(root, '.golangci.yaml')) ? 'golangci-lint run' : 'go vet ./...',
244
262
  dev: null,
263
+ format: 'gofmt -w .',
245
264
  },
246
265
  };
247
266
  }
@@ -461,6 +480,7 @@ function detectDart(root) {
461
480
  test: isFlutter ? 'flutter test' : 'dart test',
462
481
  lint: 'dart analyze',
463
482
  dev: isFlutter ? 'flutter run' : null,
483
+ format: 'dart format .',
464
484
  },
465
485
  };
466
486
  }
@@ -483,6 +503,7 @@ function detectElixir(root) {
483
503
  test: exists(root, 'test') ? 'mix test' : null,
484
504
  lint: exists(root, '.credo.exs') ? 'mix credo' : null,
485
505
  dev: null,
506
+ format: 'mix format',
486
507
  },
487
508
  };
488
509
  }
@@ -580,6 +601,19 @@ function detectDotnet(root) {
580
601
  // never match a freshly rendered one, breaking `--check`.
581
602
  const OWN_OUTPUT_FILES = new Set(['AGENTS.md', 'CLAUDE.md']);
582
603
 
604
+ function listSubprojectDirs(root) {
605
+ let entries;
606
+ try {
607
+ entries = fs.readdirSync(root, { withFileTypes: true });
608
+ } catch {
609
+ return [];
610
+ }
611
+ return entries
612
+ .filter((e) => e.isDirectory() && !e.name.startsWith('.') && !IGNORE_DIRS.has(e.name))
613
+ .map((e) => e.name)
614
+ .sort();
615
+ }
616
+
583
617
  function listStructure(root) {
584
618
  let entries;
585
619
  try {
@@ -692,12 +726,21 @@ function detectMonorepo(root) {
692
726
 
693
727
  function detectProject(root) {
694
728
  const detectors = [detectNode, detectDeno, detectPython, detectRust, detectGo, detectRuby, detectJava, detectPhp, detectDotnet, detectSwift, detectDart, detectElixir, detectCpp, detectHaskell, detectScala, detectZig, detectJulia];
695
- const stacks = detectors.map((fn) => fn(root)).filter(Boolean);
729
+ let stacks = detectors.map((fn) => fn(root)).filter(Boolean);
730
+ const monorepo = detectMonorepo(root);
731
+
732
+ // Multi-project repos that aren't JS workspaces (e.g. api/ in Go next to
733
+ // web/ in Node) have nothing at the root; look one level down instead.
734
+ if (!stacks.length && !monorepo) {
735
+ stacks = listSubprojectDirs(root).flatMap((dir) =>
736
+ detectors.map((fn) => fn(path.join(root, dir))).filter(Boolean).map((stack) => ({ ...stack, dir })),
737
+ );
738
+ }
696
739
 
697
740
  return {
698
741
  root,
699
742
  stacks,
700
- monorepo: detectMonorepo(root),
743
+ monorepo,
701
744
  structure: listStructure(root),
702
745
  ci: detectCI(root),
703
746
  license: detectLicense(root),
package/src/render.js CHANGED
@@ -27,11 +27,14 @@ function renderCommands(stack) {
27
27
  if (c.build) lines.push(`- Build: \`${c.build}\``);
28
28
  if (c.test) lines.push(`- Test: \`${c.test}\``);
29
29
  if (c.lint) lines.push(`- Lint: \`${c.lint}\``);
30
+ if (c.typecheck) lines.push(`- Typecheck: \`${c.typecheck}\``);
31
+ if (c.format) lines.push(`- Format: \`${c.format}\``);
30
32
  return lines;
31
33
  }
32
34
 
33
35
  function renderStack(stack) {
34
- const parts = [`## ${stack.ecosystem}`];
36
+ const parts = [stack.dir ? `## ${stack.ecosystem} (\`${stack.dir}/\`)` : `## ${stack.ecosystem}`];
37
+ if (stack.dir) parts.push(`Run these commands from \`${stack.dir}/\`.`, '');
35
38
  const meta = [];
36
39
  if (stack.name) meta.push(`**${stack.name}**`);
37
40
  if (stack.frameworks && stack.frameworks.length) meta.push(stack.frameworks.join(', '));