agents-md-gen 0.10.0 → 0.12.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 +29 -0
- package/README.md +11 -5
- package/package.json +7 -1
- package/src/detect.js +85 -16
- package/src/render.js +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.0
|
|
4
|
+
|
|
5
|
+
- New **Typecheck** and **Format** commands in the generated file — what an
|
|
6
|
+
agent should run before committing. Only emitted with a clear signal:
|
|
7
|
+
package.json `typecheck`/`type-check`/`tsc` and `format`/`fmt` scripts;
|
|
8
|
+
ruff or black, mypy or pyright config (Python); `cargo check`/`cargo fmt`
|
|
9
|
+
(Rust); `gofmt` (Go); `deno fmt` and deno tasks; `dart format`;
|
|
10
|
+
`mix format`.
|
|
11
|
+
- Fix: the Python lint command lacked the `poetry run`/`uv run`/`pipenv run`
|
|
12
|
+
prefix the other commands had, so it failed when ruff/flake8 only lived in
|
|
13
|
+
the project's virtualenv.
|
|
14
|
+
- README: pin a version when using `--check` in CI.
|
|
15
|
+
|
|
16
|
+
## 0.11.0
|
|
17
|
+
|
|
18
|
+
- Fix: Bun projects using the text `bun.lock` (the default since Bun 1.2)
|
|
19
|
+
were reported as npm. Both `bun.lock` and `bun.lockb` count now.
|
|
20
|
+
- Honor corepack's `packageManager` field in package.json over lockfiles.
|
|
21
|
+
- More JS frameworks: Remix, Expo, React Native, Nuxt, SvelteKit, Astro,
|
|
22
|
+
Angular, Hono, Electron; meta-frameworks replace the library they wrap
|
|
23
|
+
(Nuxt instead of Vue, SvelteKit instead of Svelte). Playwright as a test
|
|
24
|
+
runner.
|
|
25
|
+
- Detect Deno projects (`deno.json`/`deno.jsonc`, its tasks, Fresh/Hono).
|
|
26
|
+
- A lone `Pipfile` (no lock yet) is detected as a pipenv Python project.
|
|
27
|
+
- `homepage` points at the public distribution repo; it used to fall back
|
|
28
|
+
to the private source repo, which 404s for visitors.
|
|
29
|
+
- The README's "real output" example is now checked against the actual
|
|
30
|
+
CLI output by the test suite, so it can't drift again.
|
|
31
|
+
|
|
3
32
|
## 0.10.0
|
|
4
33
|
|
|
5
34
|
- Detect web frameworks in ecosystems that always reported none:
|
package/README.md
CHANGED
|
@@ -53,9 +53,12 @@ docker run --rm -v "$PWD":/workdir ghcr.io/peshino2012/agents-md-gen
|
|
|
53
53
|
|
|
54
54
|
## What it detects
|
|
55
55
|
|
|
56
|
-
- **Node.js / TypeScript** — package manager (npm/yarn/pnpm/bun
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
- **Node.js / TypeScript** — package manager (npm/yarn/pnpm/bun, honoring the
|
|
57
|
+
`packageManager` field), framework (Next.js, Remix, React, Expo/React
|
|
58
|
+
Native, Nuxt, Vue, SvelteKit, Svelte, Astro, Angular, Express, Fastify,
|
|
59
|
+
NestJS, Hono, Electron), test runner (Vitest, Jest, Mocha, Ava,
|
|
60
|
+
node:test, Playwright)
|
|
61
|
+
- **Deno** — `deno.json`/`deno.jsonc` tasks, Fresh/Hono
|
|
59
62
|
- **Python** — package manager (pip/poetry/uv/pipenv), Django/FastAPI/Flask,
|
|
60
63
|
pytest or the Django test runner, ruff
|
|
61
64
|
- **Rust** — Cargo, workspaces, Axum/Actix Web/Rocket/Tauri, clippy
|
|
@@ -121,6 +124,11 @@ longer matches what the repo would generate, so a stale file (say, a new
|
|
|
121
124
|
test framework got added but nobody regenerated it) gets caught instead of
|
|
122
125
|
silently rotting. Pair it with the GitHub Action above.
|
|
123
126
|
|
|
127
|
+
New releases detect more things, which changes the generated file — so with
|
|
128
|
+
`--check`, pin the version you generated with (`npx agents-md-gen@0.12.0
|
|
129
|
+
--check`, or the action's `version` input) and bump it on purpose, rather
|
|
130
|
+
than letting `@latest` fail CI the day a release lands.
|
|
131
|
+
|
|
124
132
|
## Example
|
|
125
133
|
|
|
126
134
|
Real output — this is what `npx agents-md-gen` produces when run against
|
|
@@ -141,7 +149,6 @@ this package's own repo:
|
|
|
141
149
|
|
|
142
150
|
## Structure
|
|
143
151
|
|
|
144
|
-
- `.github/` — GitHub config (CI, templates)
|
|
145
152
|
- `CHANGELOG.md`
|
|
146
153
|
- `Dockerfile`
|
|
147
154
|
- `LICENSE`
|
|
@@ -153,7 +160,6 @@ this package's own repo:
|
|
|
153
160
|
|
|
154
161
|
## Notes
|
|
155
162
|
|
|
156
|
-
- CI: GitHub Actions (test.yml)
|
|
157
163
|
- License: MIT
|
|
158
164
|
- Docker: Dockerfile
|
|
159
165
|
````
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agents-md-gen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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
|
+
"homepage": "https://github.com/Peshino2012/homebrew-tap#readme",
|
|
6
7
|
"funding": {
|
|
7
8
|
"type": "individual",
|
|
8
9
|
"url": "https://www.npmjs.com/package/agents-md-gen"
|
|
@@ -58,6 +59,11 @@
|
|
|
58
59
|
"fastapi",
|
|
59
60
|
"flask",
|
|
60
61
|
"rails",
|
|
62
|
+
"deno",
|
|
63
|
+
"bun",
|
|
64
|
+
"nuxt",
|
|
65
|
+
"sveltekit",
|
|
66
|
+
"astro",
|
|
61
67
|
"cli",
|
|
62
68
|
"codegen",
|
|
63
69
|
"developer-tools"
|
package/src/detect.js
CHANGED
|
@@ -31,30 +31,49 @@ function detectNode(root) {
|
|
|
31
31
|
const pkg = readJson(root, 'package.json');
|
|
32
32
|
if (!pkg) return null;
|
|
33
33
|
|
|
34
|
+
// corepack's `packageManager` field ("pnpm@9.12.0") is explicit, so it wins
|
|
35
|
+
// over lockfile sniffing -- and it's the only signal when no lockfile is
|
|
36
|
+
// committed.
|
|
37
|
+
const declared = typeof pkg.packageManager === 'string' ? pkg.packageManager.split('@')[0] : null;
|
|
34
38
|
let packageManager = 'npm';
|
|
35
|
-
if (
|
|
39
|
+
if (['npm', 'pnpm', 'yarn', 'bun'].includes(declared)) packageManager = declared;
|
|
40
|
+
else if (exists(root, 'pnpm-lock.yaml')) packageManager = 'pnpm';
|
|
36
41
|
else if (exists(root, 'yarn.lock')) packageManager = 'yarn';
|
|
37
|
-
else if (exists(root, 'bun.lockb')) packageManager = 'bun';
|
|
42
|
+
else if (exists(root, 'bun.lock') || exists(root, 'bun.lockb')) packageManager = 'bun';
|
|
38
43
|
|
|
39
44
|
const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
|
|
40
45
|
const has = (name) => Object.prototype.hasOwnProperty.call(deps, name);
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
// Meta-frameworks are listed instead of the library they're built on
|
|
48
|
+
// (Nuxt, not Vue; SvelteKit, not Svelte).
|
|
49
|
+
const isRemix = has('@remix-run/react') || has('@remix-run/node');
|
|
50
|
+
const frameworks = [
|
|
51
|
+
has('next') && 'Next.js',
|
|
52
|
+
isRemix && 'Remix',
|
|
53
|
+
has('expo') && 'Expo',
|
|
54
|
+
has('react-native') && !has('expo') && 'React Native',
|
|
55
|
+
has('react') && !has('next') && !isRemix && !has('react-native') && 'React',
|
|
56
|
+
has('nuxt') && 'Nuxt',
|
|
57
|
+
has('vue') && !has('nuxt') && 'Vue',
|
|
58
|
+
has('@sveltejs/kit') && 'SvelteKit',
|
|
59
|
+
has('svelte') && !has('@sveltejs/kit') && 'Svelte',
|
|
60
|
+
has('astro') && 'Astro',
|
|
61
|
+
has('@angular/core') && 'Angular',
|
|
62
|
+
has('express') && 'Express',
|
|
63
|
+
has('fastify') && 'Fastify',
|
|
64
|
+
has('@nestjs/core') && 'NestJS',
|
|
65
|
+
has('hono') && 'Hono',
|
|
66
|
+
has('electron') && 'Electron',
|
|
67
|
+
has('vite') && 'Vite',
|
|
68
|
+
has('typescript') && 'TypeScript',
|
|
69
|
+
].filter(Boolean);
|
|
52
70
|
|
|
53
71
|
const testFramework = has('vitest') ? 'vitest'
|
|
54
72
|
: has('jest') ? 'jest'
|
|
55
73
|
: has('mocha') ? 'mocha'
|
|
56
74
|
: has('ava') ? 'ava'
|
|
57
75
|
: (pkg.scripts && pkg.scripts.test && pkg.scripts.test.includes('node --test')) ? 'node:test'
|
|
76
|
+
: has('@playwright/test') ? 'playwright'
|
|
58
77
|
: null;
|
|
59
78
|
|
|
60
79
|
const run = (script) => (pkg.scripts && pkg.scripts[script]) ? `${packageManager} run ${script}` : null;
|
|
@@ -65,6 +84,8 @@ function detectNode(root) {
|
|
|
65
84
|
test: run('test'),
|
|
66
85
|
lint: run('lint'),
|
|
67
86
|
dev: run('dev') || run('start'),
|
|
87
|
+
typecheck: run('typecheck') || run('type-check') || run('tsc'),
|
|
88
|
+
format: run('format') || run('fmt'),
|
|
68
89
|
};
|
|
69
90
|
|
|
70
91
|
return {
|
|
@@ -78,16 +99,47 @@ function detectNode(root) {
|
|
|
78
99
|
};
|
|
79
100
|
}
|
|
80
101
|
|
|
102
|
+
function detectDeno(root) {
|
|
103
|
+
const file = ['deno.json', 'deno.jsonc'].find((f) => exists(root, f));
|
|
104
|
+
if (!file) return null;
|
|
105
|
+
const config = readJsonWithComments(root, file) || {};
|
|
106
|
+
const tasks = config.tasks || {};
|
|
107
|
+
const task = (name) => (tasks[name] ? `deno task ${name}` : null);
|
|
108
|
+
const text = readText(root, file) || '';
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
ecosystem: 'Deno',
|
|
112
|
+
name: config.name || null,
|
|
113
|
+
description: null,
|
|
114
|
+
packageManager: 'deno',
|
|
115
|
+
frameworks: [
|
|
116
|
+
/\$fresh\/|@fresh\/core/.test(text) && 'Fresh',
|
|
117
|
+
/\bhono\b/.test(text) && 'Hono',
|
|
118
|
+
].filter(Boolean),
|
|
119
|
+
testFramework: 'deno test',
|
|
120
|
+
commands: {
|
|
121
|
+
install: null,
|
|
122
|
+
build: task('build'),
|
|
123
|
+
test: task('test') || 'deno test',
|
|
124
|
+
lint: task('lint') || 'deno lint',
|
|
125
|
+
dev: task('dev') || task('start'),
|
|
126
|
+
typecheck: task('check') || task('typecheck'),
|
|
127
|
+
format: task('fmt') || task('format') || 'deno fmt',
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
81
132
|
function detectPython(root) {
|
|
82
133
|
const hasPyproject = exists(root, 'pyproject.toml');
|
|
83
134
|
const hasRequirements = exists(root, 'requirements.txt');
|
|
84
135
|
const hasSetupPy = exists(root, 'setup.py');
|
|
85
|
-
|
|
136
|
+
const hasPipfile = exists(root, 'Pipfile');
|
|
137
|
+
if (!hasPyproject && !hasRequirements && !hasSetupPy && !hasPipfile) return null;
|
|
86
138
|
|
|
87
139
|
let packageManager = 'pip';
|
|
88
140
|
if (exists(root, 'poetry.lock')) packageManager = 'poetry';
|
|
89
141
|
else if (exists(root, 'uv.lock')) packageManager = 'uv';
|
|
90
|
-
else if (
|
|
142
|
+
else if (hasPipfile) packageManager = 'pipenv';
|
|
91
143
|
|
|
92
144
|
const pyproject = readText(root, 'pyproject.toml') || '';
|
|
93
145
|
const usesPytest = exists(root, 'pytest.ini') || exists(root, 'conftest.py') || /pytest/.test(pyproject);
|
|
@@ -114,6 +166,16 @@ function detectPython(root) {
|
|
|
114
166
|
|
|
115
167
|
const run = { poetry: 'poetry run ', uv: 'uv run ', pipenv: 'pipenv run ' }[packageManager] || '';
|
|
116
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
|
+
|
|
117
179
|
let testCmd = null;
|
|
118
180
|
if (usesPytest) testCmd = `${run}pytest`;
|
|
119
181
|
else if (hasManagePy) testCmd = `${run}python manage.py test`;
|
|
@@ -129,8 +191,10 @@ function detectPython(root) {
|
|
|
129
191
|
install: installCmd,
|
|
130
192
|
build: null,
|
|
131
193
|
test: testCmd,
|
|
132
|
-
lint: usesRuff ?
|
|
194
|
+
lint: usesRuff ? `${run}ruff check .` : (exists(root, '.flake8') ? `${run}flake8` : null),
|
|
133
195
|
dev: hasManagePy ? `${run}python manage.py runserver` : null,
|
|
196
|
+
typecheck: typecheckCmd,
|
|
197
|
+
format: formatCmd,
|
|
134
198
|
},
|
|
135
199
|
};
|
|
136
200
|
}
|
|
@@ -167,6 +231,8 @@ function detectRust(root) {
|
|
|
167
231
|
test: 'cargo test',
|
|
168
232
|
lint: 'cargo clippy',
|
|
169
233
|
dev: null,
|
|
234
|
+
typecheck: 'cargo check',
|
|
235
|
+
format: 'cargo fmt',
|
|
170
236
|
},
|
|
171
237
|
};
|
|
172
238
|
}
|
|
@@ -194,6 +260,7 @@ function detectGo(root) {
|
|
|
194
260
|
test: 'go test ./...',
|
|
195
261
|
lint: (exists(root, '.golangci.yml') || exists(root, '.golangci.yaml')) ? 'golangci-lint run' : 'go vet ./...',
|
|
196
262
|
dev: null,
|
|
263
|
+
format: 'gofmt -w .',
|
|
197
264
|
},
|
|
198
265
|
};
|
|
199
266
|
}
|
|
@@ -413,6 +480,7 @@ function detectDart(root) {
|
|
|
413
480
|
test: isFlutter ? 'flutter test' : 'dart test',
|
|
414
481
|
lint: 'dart analyze',
|
|
415
482
|
dev: isFlutter ? 'flutter run' : null,
|
|
483
|
+
format: 'dart format .',
|
|
416
484
|
},
|
|
417
485
|
};
|
|
418
486
|
}
|
|
@@ -435,6 +503,7 @@ function detectElixir(root) {
|
|
|
435
503
|
test: exists(root, 'test') ? 'mix test' : null,
|
|
436
504
|
lint: exists(root, '.credo.exs') ? 'mix credo' : null,
|
|
437
505
|
dev: null,
|
|
506
|
+
format: 'mix format',
|
|
438
507
|
},
|
|
439
508
|
};
|
|
440
509
|
}
|
|
@@ -643,7 +712,7 @@ function detectMonorepo(root) {
|
|
|
643
712
|
}
|
|
644
713
|
|
|
645
714
|
function detectProject(root) {
|
|
646
|
-
const detectors = [detectNode, detectPython, detectRust, detectGo, detectRuby, detectJava, detectPhp, detectDotnet, detectSwift, detectDart, detectElixir, detectCpp, detectHaskell, detectScala, detectZig, detectJulia];
|
|
715
|
+
const detectors = [detectNode, detectDeno, detectPython, detectRust, detectGo, detectRuby, detectJava, detectPhp, detectDotnet, detectSwift, detectDart, detectElixir, detectCpp, detectHaskell, detectScala, detectZig, detectJulia];
|
|
647
716
|
const stacks = detectors.map((fn) => fn(root)).filter(Boolean);
|
|
648
717
|
|
|
649
718
|
return {
|
package/src/render.js
CHANGED
|
@@ -27,6 +27,8 @@ 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
|
|