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 +21 -0
- package/README.md +8 -0
- package/package.json +1 -1
- package/src/detect.js +46 -3
- package/src/render.js +4 -1
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.
|
|
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 ?
|
|
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
|
-
|
|
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
|
|
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(', '));
|