agents-md-gen 0.9.0 → 0.10.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,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.0
4
+
5
+ - Detect web frameworks in ecosystems that always reported none:
6
+ Django/FastAPI/Flask (Python), Rails/Sinatra (Ruby), Gin/Echo/Fiber/chi
7
+ (Go), Axum/Actix Web/Rocket/Tauri (Rust).
8
+ - Django projects get `python manage.py runserver` as the dev command and
9
+ `python manage.py test` when pytest isn't set up; Rails apps get
10
+ `bin/rails server` and `bin/rails test`.
11
+ - Python commands are prefixed with `pipenv run` for pipenv projects (like
12
+ `poetry run`/`uv run` already were).
13
+ - README: the Scoop install command pointed at a private repo and 404'd;
14
+ it now adds the public `Peshino2012/homebrew-tap` bucket.
15
+
16
+ ## 0.9.1
17
+
18
+ - README: the GitHub Action moved to `Peshino2012/homebrew-tap/action`,
19
+ a public repo. The previous `uses:` line pointed at a private repo,
20
+ which other repos cannot use.
21
+
3
22
  ## 0.9.0
4
23
 
5
24
  - Point `funding`/homepage links at the public npm page (they pointed at
package/README.md CHANGED
@@ -14,7 +14,8 @@ on immediately.
14
14
  On Windows, you can also install it with [Scoop](https://scoop.sh):
15
15
 
16
16
  ```
17
- scoop install https://raw.githubusercontent.com/Peshino2012/PD1/claude/busy-newton-5h97oh/scoop-bucket/agents-md-gen.json
17
+ scoop bucket add peshino2012 https://github.com/Peshino2012/homebrew-tap
18
+ scoop install peshino2012/agents-md-gen
18
19
  ```
19
20
 
20
21
  On macOS/Linux, install it with [Homebrew](https://brew.sh):
@@ -55,10 +56,11 @@ docker run --rm -v "$PWD":/workdir ghcr.io/peshino2012/agents-md-gen
55
56
  - **Node.js / TypeScript** — package manager (npm/yarn/pnpm/bun), framework
56
57
  (React, Next.js, Vue, Svelte, Express, Fastify, NestJS), test runner
57
58
  (Jest, Vitest, Mocha, Ava, node:test)
58
- - **Python** — package manager (pip/poetry/uv/pipenv), pytest, ruff
59
- - **Rust** — Cargo, workspaces, clippy
60
- - **Go** — go.mod, golangci-lint
61
- - **Ruby** — Bundler, RSpec
59
+ - **Python** — package manager (pip/poetry/uv/pipenv), Django/FastAPI/Flask,
60
+ pytest or the Django test runner, ruff
61
+ - **Rust** — Cargo, workspaces, Axum/Actix Web/Rocket/Tauri, clippy
62
+ - **Go** — go.mod, Gin/Echo/Fiber/chi, golangci-lint
63
+ - **Ruby** — Bundler, Rails/Sinatra, RSpec or minitest
62
64
  - **Java/Kotlin** — Maven, Gradle (with wrapper detection)
63
65
  - **PHP** — Composer, Laravel/Symfony detection, PHPUnit
64
66
  - **.NET/C#** — NuGet, xUnit/NUnit/MSTest detection
@@ -79,13 +81,22 @@ docker run --rm -v "$PWD":/workdir ghcr.io/peshino2012/agents-md-gen
79
81
  Use it directly in CI without installing anything:
80
82
 
81
83
  ```yaml
82
- - uses: Peshino2012/PD1@claude/busy-newton-5h97oh
84
+ - uses: Peshino2012/homebrew-tap/action@main
83
85
  with:
84
86
  directory: .
85
87
  also-claude-md: 'true'
86
88
  ```
87
89
 
88
- (pin to a tagged release instead of a branch once one exists)
90
+ Or fail CI when `AGENTS.md` goes stale:
91
+
92
+ ```yaml
93
+ - uses: Peshino2012/homebrew-tap/action@main
94
+ with:
95
+ check: 'true'
96
+ ```
97
+
98
+ Inputs: `directory`, `output`, `also-claude-md`, `force`, `check`, and
99
+ `version` (defaults to `latest`; set it to pin a release).
89
100
 
90
101
  A repo can match more than one of these (e.g. a Python backend next to a
91
102
  Node frontend) — each detected stack gets its own section.
@@ -108,7 +119,7 @@ clobber one you already wrote.
108
119
  `--check` is for CI: it fails the build if `AGENTS.md` doesn't exist or no
109
120
  longer matches what the repo would generate, so a stale file (say, a new
110
121
  test framework got added but nobody regenerated it) gets caught instead of
111
- silently rotting. Pair it with the GitHub Action below.
122
+ silently rotting. Pair it with the GitHub Action above.
112
123
 
113
124
  ## Example
114
125
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-md-gen",
3
- "version": "0.9.0",
3
+ "version": "0.10.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
  "funding": {
@@ -54,6 +54,10 @@
54
54
  "scala",
55
55
  "zig",
56
56
  "julia",
57
+ "django",
58
+ "fastapi",
59
+ "flask",
60
+ "rails",
57
61
  "cli",
58
62
  "codegen",
59
63
  "developer-tools"
package/src/detect.js CHANGED
@@ -94,6 +94,17 @@ function detectPython(root) {
94
94
  const nameMatch = pyproject.match(/^name\s*=\s*"([^"]+)"/m);
95
95
  const usesRuff = /\bruff\b/.test(pyproject) || exists(root, 'ruff.toml');
96
96
 
97
+ const depsText = [pyproject, readText(root, 'requirements.txt'), readText(root, 'Pipfile')]
98
+ .filter(Boolean)
99
+ .join('\n');
100
+ const hasManagePy = exists(root, 'manage.py');
101
+ const usesDjango = hasManagePy || hasPythonDep(depsText, 'django');
102
+ const frameworks = [
103
+ usesDjango ? 'Django' : null,
104
+ hasPythonDep(depsText, 'fastapi') ? 'FastAPI' : null,
105
+ hasPythonDep(depsText, 'flask') ? 'Flask' : null,
106
+ ].filter(Boolean);
107
+
97
108
  const installCmd = {
98
109
  poetry: 'poetry install',
99
110
  uv: 'uv sync',
@@ -101,38 +112,54 @@ function detectPython(root) {
101
112
  pip: hasPyproject ? 'pip install -e .' : 'pip install -r requirements.txt',
102
113
  }[packageManager];
103
114
 
104
- const testCmd = usesPytest
105
- ? (packageManager === 'poetry' ? 'poetry run pytest' : packageManager === 'uv' ? 'uv run pytest' : 'pytest')
106
- : null;
115
+ const run = { poetry: 'poetry run ', uv: 'uv run ', pipenv: 'pipenv run ' }[packageManager] || '';
116
+
117
+ let testCmd = null;
118
+ if (usesPytest) testCmd = `${run}pytest`;
119
+ else if (hasManagePy) testCmd = `${run}python manage.py test`;
107
120
 
108
121
  return {
109
122
  ecosystem: 'Python',
110
123
  name: nameMatch ? nameMatch[1] : null,
111
124
  description: null,
112
125
  packageManager,
113
- frameworks: [],
114
- testFramework: usesPytest ? 'pytest' : null,
126
+ frameworks,
127
+ testFramework: usesPytest ? 'pytest' : (hasManagePy ? 'Django test runner' : null),
115
128
  commands: {
116
129
  install: installCmd,
117
130
  build: null,
118
131
  test: testCmd,
119
132
  lint: usesRuff ? 'ruff check .' : (exists(root, '.flake8') ? 'flake8' : null),
120
- dev: null,
133
+ dev: hasManagePy ? `${run}python manage.py runserver` : null,
121
134
  },
122
135
  };
123
136
  }
124
137
 
138
+ // Matches a PyPI requirement for exactly `name` in pyproject/requirements/
139
+ // Pipfile text: `django>=5`, `"Django[argon2]"`, `django = "*"` -- but not
140
+ // `django-environ` or `djangorestframework`.
141
+ function hasPythonDep(text, name) {
142
+ return new RegExp(`(^|["'\\s,\\[])${name}(\\s*[<>=~!\\[;"',]|\\s*$)`, 'im').test(text);
143
+ }
144
+
125
145
  function detectRust(root) {
126
146
  const cargoToml = readText(root, 'Cargo.toml');
127
147
  if (!cargoToml) return null;
128
148
  const nameMatch = cargoToml.match(/^name\s*=\s*"([^"]+)"/m);
129
149
  const isWorkspace = /^\[workspace\]/m.test(cargoToml);
150
+ const hasCrate = (name) => new RegExp(`^\\s*${name}\\s*=`, 'm').test(cargoToml);
151
+ const rustFrameworks = [
152
+ ['axum', 'Axum'],
153
+ ['actix-web', 'Actix Web'],
154
+ ['rocket', 'Rocket'],
155
+ ['tauri', 'Tauri'],
156
+ ].filter(([crate]) => hasCrate(crate)).map(([, label]) => label);
130
157
  return {
131
158
  ecosystem: 'Rust',
132
159
  name: nameMatch ? nameMatch[1] : null,
133
160
  description: null,
134
161
  packageManager: 'cargo',
135
- frameworks: isWorkspace ? ['Cargo workspace'] : [],
162
+ frameworks: [...(isWorkspace ? ['Cargo workspace'] : []), ...rustFrameworks],
136
163
  testFramework: 'cargo test',
137
164
  commands: {
138
165
  install: 'cargo build',
@@ -148,12 +175,18 @@ function detectGo(root) {
148
175
  const goMod = readText(root, 'go.mod');
149
176
  if (!goMod) return null;
150
177
  const moduleMatch = goMod.match(/^module\s+(\S+)/m);
178
+ const goFrameworks = [
179
+ ['github.com/gin-gonic/gin', 'Gin'],
180
+ ['github.com/labstack/echo', 'Echo'],
181
+ ['github.com/gofiber/fiber', 'Fiber'],
182
+ ['github.com/go-chi/chi', 'chi'],
183
+ ];
151
184
  return {
152
185
  ecosystem: 'Go',
153
186
  name: moduleMatch ? moduleMatch[1] : null,
154
187
  description: null,
155
188
  packageManager: 'go modules',
156
- frameworks: [],
189
+ frameworks: goFrameworks.filter(([mod]) => goMod.includes(mod)).map(([, label]) => label),
157
190
  testFramework: 'go test',
158
191
  commands: {
159
192
  install: 'go mod download',
@@ -210,21 +243,31 @@ function detectJava(root) {
210
243
  }
211
244
 
212
245
  function detectRuby(root) {
213
- if (!exists(root, 'Gemfile')) return null;
246
+ const gemfile = readText(root, 'Gemfile');
247
+ if (gemfile === null) return null;
214
248
  const hasRspec = exists(root, '.rspec') || exists(root, 'spec');
249
+ const hasGem = (name) => new RegExp(`^\\s*gem\\s+["']${name}["']`, 'm').test(gemfile);
250
+ const isRails = hasGem('rails') || exists(root, 'bin', 'rails');
251
+ const rails = exists(root, 'bin', 'rails') ? 'bin/rails' : 'bundle exec rails';
252
+
253
+ let testCmd = null;
254
+ if (hasRspec) testCmd = 'bundle exec rspec';
255
+ else if (isRails) testCmd = `${rails} test`;
256
+ else if (exists(root, 'Rakefile')) testCmd = 'bundle exec rake test';
257
+
215
258
  return {
216
259
  ecosystem: 'Ruby',
217
260
  name: null,
218
261
  description: null,
219
262
  packageManager: 'bundler',
220
- frameworks: [],
221
- testFramework: hasRspec ? 'rspec' : null,
263
+ frameworks: [isRails ? 'Rails' : null, hasGem('sinatra') ? 'Sinatra' : null].filter(Boolean),
264
+ testFramework: hasRspec ? 'rspec' : (isRails ? 'minitest' : null),
222
265
  commands: {
223
266
  install: 'bundle install',
224
267
  build: null,
225
- test: hasRspec ? 'bundle exec rspec' : (exists(root, 'Rakefile') ? 'bundle exec rake test' : null),
268
+ test: testCmd,
226
269
  lint: exists(root, '.rubocop.yml') ? 'bundle exec rubocop' : null,
227
- dev: null,
270
+ dev: isRails ? `${rails} server` : null,
228
271
  },
229
272
  };
230
273
  }