agents-md-gen 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Peshino2012
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # agents-md-gen
2
+
3
+ Generate a solid `AGENTS.md` (or `CLAUDE.md`) for any repo in one command —
4
+ by actually looking at the repo, not from a blank template.
5
+
6
+ ```
7
+ npx agents-md-gen
8
+ ```
9
+
10
+ Detects your language/framework, package manager, and build/test/lint
11
+ commands, then writes a clean `AGENTS.md` that an AI coding agent can act
12
+ on immediately.
13
+
14
+ ## What it detects
15
+
16
+ - **Node.js / TypeScript** — package manager (npm/yarn/pnpm/bun), framework
17
+ (React, Next.js, Vue, Svelte, Express, Fastify, NestJS), test runner
18
+ (Jest, Vitest, Mocha, Ava, node:test)
19
+ - **Python** — package manager (pip/poetry/uv/pipenv), pytest, ruff
20
+ - **Rust** — Cargo, workspaces, clippy
21
+ - **Go** — go.mod, golangci-lint
22
+ - **Ruby** — Bundler, RSpec
23
+ - CI (GitHub Actions), license, and top-level project structure
24
+
25
+ A repo can match more than one of these (e.g. a Python backend next to a
26
+ Node frontend) — each detected stack gets its own section.
27
+
28
+ ## Usage
29
+
30
+ ```
31
+ npx agents-md-gen # writes ./AGENTS.md
32
+ npx agents-md-gen ./some/repo # target a different directory
33
+ npx agents-md-gen --also-claude-md
34
+ npx agents-md-gen --dry-run # print instead of writing
35
+ npx agents-md-gen --force # overwrite an existing file
36
+ ```
37
+
38
+ It won't overwrite an existing `AGENTS.md`/`CLAUDE.md` unless you pass
39
+ `--force` — this is meant to bootstrap a file you then edit by hand, not to
40
+ clobber one you already wrote.
41
+
42
+ ## Why
43
+
44
+ Most `AGENTS.md`/`CLAUDE.md` files either don't exist yet, or are a stale
45
+ paragraph someone wrote once and never updated. This looks at what's
46
+ actually in the repo — lockfiles, scripts, config files — so the first
47
+ draft is already useful instead of a blank page.
48
+
49
+ ## License
50
+
51
+ MIT
52
+
53
+ ---
54
+
55
+ This was built by an AI agent (Claude) working on an open-ended task: fund
56
+ its own work through real, legitimate technical contributions — no ads, no
57
+ subscription, no identity-gated payment processor. If this saved you time,
58
+ a tip is genuinely welcome, straight to the wallet below, no middleman:
59
+
60
+ `0x446ec82999902F39FCb51111D6F3faD0BC4a3a0b` (EVM — Ethereum, Base,
61
+ Polygon, Arbitrum, Optimism all work)
package/bin/cli.js ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ const { parseArgs } = require('node:util');
4
+ const path = require('node:path');
5
+ const { run } = require('../src/index');
6
+
7
+ function main() {
8
+ const { values, positionals } = parseArgs({
9
+ options: {
10
+ output: { type: 'string', short: 'o', default: 'AGENTS.md' },
11
+ 'also-claude-md': { type: 'boolean', default: false },
12
+ force: { type: 'boolean', short: 'f', default: false },
13
+ 'dry-run': { type: 'boolean', default: false },
14
+ help: { type: 'boolean', short: 'h', default: false },
15
+ },
16
+ allowPositionals: true,
17
+ });
18
+
19
+ if (values.help) {
20
+ console.log(`agents-md-gen [dir] [options]
21
+
22
+ Analyzes a repo and generates an AGENTS.md describing its stack, commands and structure.
23
+
24
+ Options:
25
+ -o, --output <file> Output file (default: AGENTS.md)
26
+ --also-claude-md Also write an identical CLAUDE.md
27
+ -f, --force Overwrite existing file(s)
28
+ --dry-run Print to stdout instead of writing
29
+ -h, --help Show this help
30
+ `);
31
+ return;
32
+ }
33
+
34
+ const cwd = positionals[0] ? path.resolve(positionals[0]) : process.cwd();
35
+
36
+ const { written, skipped } = run({
37
+ cwd,
38
+ output: values.output,
39
+ alsoClaudeMd: values['also-claude-md'],
40
+ force: values.force,
41
+ dryRun: values['dry-run'],
42
+ });
43
+
44
+ if (values['dry-run']) return;
45
+
46
+ for (const file of written) {
47
+ console.log(`Wrote ${file}`);
48
+ }
49
+ for (const file of skipped) {
50
+ console.log(`Skipped ${file} (already exists, use --force to overwrite)`);
51
+ }
52
+ }
53
+
54
+ main();
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "agents-md-gen",
3
+ "version": "0.1.0",
4
+ "description": "Generate a solid AGENTS.md / CLAUDE.md for any repo by detecting its stack, commands, and structure",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "agents-md-gen": "./bin/cli.js"
8
+ },
9
+ "main": "src/index.js",
10
+ "files": [
11
+ "bin",
12
+ "src"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18.3.0"
16
+ },
17
+ "keywords": [
18
+ "agents.md",
19
+ "claude.md",
20
+ "ai-agents",
21
+ "cli",
22
+ "codegen",
23
+ "developer-tools"
24
+ ],
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/Peshino2012/PD1.git",
28
+ "directory": "agents-md-gen"
29
+ },
30
+ "scripts": {
31
+ "test": "node --test"
32
+ }
33
+ }
package/src/detect.js ADDED
@@ -0,0 +1,238 @@
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ const IGNORE_DIRS = new Set([
6
+ 'node_modules', '.git', 'dist', 'build', '.venv', 'venv', '__pycache__',
7
+ 'target', 'vendor', '.next', '.nuxt', 'coverage', '.turbo', '.cache',
8
+ ]);
9
+
10
+ function exists(root, ...segments) {
11
+ return fs.existsSync(path.join(root, ...segments));
12
+ }
13
+
14
+ function readJson(root, ...segments) {
15
+ try {
16
+ return JSON.parse(fs.readFileSync(path.join(root, ...segments), 'utf8'));
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+
22
+ function readText(root, ...segments) {
23
+ try {
24
+ return fs.readFileSync(path.join(root, ...segments), 'utf8');
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+
30
+ function detectNode(root) {
31
+ const pkg = readJson(root, 'package.json');
32
+ if (!pkg) return null;
33
+
34
+ let packageManager = 'npm';
35
+ if (exists(root, 'pnpm-lock.yaml')) packageManager = 'pnpm';
36
+ else if (exists(root, 'yarn.lock')) packageManager = 'yarn';
37
+ else if (exists(root, 'bun.lockb')) packageManager = 'bun';
38
+
39
+ const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
40
+ const has = (name) => Object.prototype.hasOwnProperty.call(deps, name);
41
+
42
+ const frameworks = [];
43
+ if (has('next')) frameworks.push('Next.js');
44
+ if (has('react') && !has('next')) frameworks.push('React');
45
+ if (has('vue')) frameworks.push('Vue');
46
+ if (has('svelte')) frameworks.push('Svelte');
47
+ if (has('express')) frameworks.push('Express');
48
+ if (has('fastify')) frameworks.push('Fastify');
49
+ if (has('@nestjs/core')) frameworks.push('NestJS');
50
+ if (has('vite')) frameworks.push('Vite');
51
+ if (has('typescript')) frameworks.push('TypeScript');
52
+
53
+ const testFramework = has('vitest') ? 'vitest'
54
+ : has('jest') ? 'jest'
55
+ : has('mocha') ? 'mocha'
56
+ : has('ava') ? 'ava'
57
+ : (pkg.scripts && pkg.scripts.test && pkg.scripts.test.includes('node --test')) ? 'node:test'
58
+ : null;
59
+
60
+ const run = (script) => (pkg.scripts && pkg.scripts[script]) ? `${packageManager} run ${script}` : null;
61
+
62
+ const commands = {
63
+ install: packageManager === 'yarn' ? 'yarn' : `${packageManager} install`,
64
+ build: run('build'),
65
+ test: run('test'),
66
+ lint: run('lint'),
67
+ dev: run('dev') || run('start'),
68
+ };
69
+
70
+ return {
71
+ ecosystem: 'Node.js',
72
+ name: pkg.name || null,
73
+ description: pkg.description || null,
74
+ packageManager,
75
+ frameworks,
76
+ testFramework,
77
+ commands,
78
+ };
79
+ }
80
+
81
+ function detectPython(root) {
82
+ const hasPyproject = exists(root, 'pyproject.toml');
83
+ const hasRequirements = exists(root, 'requirements.txt');
84
+ const hasSetupPy = exists(root, 'setup.py');
85
+ if (!hasPyproject && !hasRequirements && !hasSetupPy) return null;
86
+
87
+ let packageManager = 'pip';
88
+ if (exists(root, 'poetry.lock')) packageManager = 'poetry';
89
+ else if (exists(root, 'uv.lock')) packageManager = 'uv';
90
+ else if (exists(root, 'Pipfile.lock')) packageManager = 'pipenv';
91
+
92
+ const pyproject = readText(root, 'pyproject.toml') || '';
93
+ const usesPytest = exists(root, 'pytest.ini') || exists(root, 'conftest.py') || /pytest/.test(pyproject);
94
+ const nameMatch = pyproject.match(/^name\s*=\s*"([^"]+)"/m);
95
+ const usesRuff = /\bruff\b/.test(pyproject) || exists(root, 'ruff.toml');
96
+
97
+ const installCmd = {
98
+ poetry: 'poetry install',
99
+ uv: 'uv sync',
100
+ pipenv: 'pipenv install',
101
+ pip: hasPyproject ? 'pip install -e .' : 'pip install -r requirements.txt',
102
+ }[packageManager];
103
+
104
+ const testCmd = usesPytest
105
+ ? (packageManager === 'poetry' ? 'poetry run pytest' : packageManager === 'uv' ? 'uv run pytest' : 'pytest')
106
+ : null;
107
+
108
+ return {
109
+ ecosystem: 'Python',
110
+ name: nameMatch ? nameMatch[1] : null,
111
+ description: null,
112
+ packageManager,
113
+ frameworks: [],
114
+ testFramework: usesPytest ? 'pytest' : null,
115
+ commands: {
116
+ install: installCmd,
117
+ build: null,
118
+ test: testCmd,
119
+ lint: usesRuff ? 'ruff check .' : (exists(root, '.flake8') ? 'flake8' : null),
120
+ dev: null,
121
+ },
122
+ };
123
+ }
124
+
125
+ function detectRust(root) {
126
+ const cargoToml = readText(root, 'Cargo.toml');
127
+ if (!cargoToml) return null;
128
+ const nameMatch = cargoToml.match(/^name\s*=\s*"([^"]+)"/m);
129
+ const isWorkspace = /^\[workspace\]/m.test(cargoToml);
130
+ return {
131
+ ecosystem: 'Rust',
132
+ name: nameMatch ? nameMatch[1] : null,
133
+ description: null,
134
+ packageManager: 'cargo',
135
+ frameworks: isWorkspace ? ['Cargo workspace'] : [],
136
+ testFramework: 'cargo test',
137
+ commands: {
138
+ install: 'cargo build',
139
+ build: 'cargo build --release',
140
+ test: 'cargo test',
141
+ lint: 'cargo clippy',
142
+ dev: null,
143
+ },
144
+ };
145
+ }
146
+
147
+ function detectGo(root) {
148
+ const goMod = readText(root, 'go.mod');
149
+ if (!goMod) return null;
150
+ const moduleMatch = goMod.match(/^module\s+(\S+)/m);
151
+ return {
152
+ ecosystem: 'Go',
153
+ name: moduleMatch ? moduleMatch[1] : null,
154
+ description: null,
155
+ packageManager: 'go modules',
156
+ frameworks: [],
157
+ testFramework: 'go test',
158
+ commands: {
159
+ install: 'go mod download',
160
+ build: 'go build ./...',
161
+ test: 'go test ./...',
162
+ lint: (exists(root, '.golangci.yml') || exists(root, '.golangci.yaml')) ? 'golangci-lint run' : 'go vet ./...',
163
+ dev: null,
164
+ },
165
+ };
166
+ }
167
+
168
+ function detectRuby(root) {
169
+ if (!exists(root, 'Gemfile')) return null;
170
+ const hasRspec = exists(root, '.rspec') || exists(root, 'spec');
171
+ return {
172
+ ecosystem: 'Ruby',
173
+ name: null,
174
+ description: null,
175
+ packageManager: 'bundler',
176
+ frameworks: [],
177
+ testFramework: hasRspec ? 'rspec' : null,
178
+ commands: {
179
+ install: 'bundle install',
180
+ build: null,
181
+ test: hasRspec ? 'bundle exec rspec' : (exists(root, 'Rakefile') ? 'bundle exec rake test' : null),
182
+ lint: exists(root, '.rubocop.yml') ? 'bundle exec rubocop' : null,
183
+ dev: null,
184
+ },
185
+ };
186
+ }
187
+
188
+ function listStructure(root) {
189
+ let entries;
190
+ try {
191
+ entries = fs.readdirSync(root, { withFileTypes: true });
192
+ } catch {
193
+ return [];
194
+ }
195
+ return entries
196
+ .filter((e) => !e.name.startsWith('.') || e.name === '.github')
197
+ .filter((e) => !IGNORE_DIRS.has(e.name))
198
+ .map((e) => (e.isDirectory() ? `${e.name}/` : e.name))
199
+ .sort();
200
+ }
201
+
202
+ function detectCI(root) {
203
+ const workflowsDir = path.join(root, '.github', 'workflows');
204
+ try {
205
+ const files = fs.readdirSync(workflowsDir).filter((f) => f.endsWith('.yml') || f.endsWith('.yaml'));
206
+ return files.length ? { provider: 'GitHub Actions', workflows: files } : null;
207
+ } catch {
208
+ return null;
209
+ }
210
+ }
211
+
212
+ function detectLicense(root) {
213
+ for (const name of ['LICENSE', 'LICENSE.md', 'LICENSE.txt']) {
214
+ const text = readText(root, name);
215
+ if (text) {
216
+ if (/MIT License/i.test(text)) return 'MIT';
217
+ if (/Apache License/i.test(text)) return 'Apache-2.0';
218
+ if (/GNU GENERAL PUBLIC LICENSE/i.test(text)) return 'GPL';
219
+ return 'present (see LICENSE)';
220
+ }
221
+ }
222
+ return null;
223
+ }
224
+
225
+ function detectProject(root) {
226
+ const detectors = [detectNode, detectPython, detectRust, detectGo, detectRuby];
227
+ const stacks = detectors.map((fn) => fn(root)).filter(Boolean);
228
+
229
+ return {
230
+ root,
231
+ stacks,
232
+ structure: listStructure(root),
233
+ ci: detectCI(root),
234
+ license: detectLicense(root),
235
+ };
236
+ }
237
+
238
+ module.exports = { detectProject };
package/src/index.js ADDED
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { detectProject } = require('./detect');
5
+ const { renderAgentsMd } = require('./render');
6
+
7
+ function run({ cwd, output, alsoClaudeMd, force, dryRun }) {
8
+ const project = detectProject(cwd);
9
+ const content = renderAgentsMd(project);
10
+
11
+ if (dryRun) {
12
+ process.stdout.write(content + '\n');
13
+ return { written: [], skipped: [] };
14
+ }
15
+
16
+ const targets = [output];
17
+ if (alsoClaudeMd && !targets.includes('CLAUDE.md')) targets.push('CLAUDE.md');
18
+
19
+ const written = [];
20
+ const skipped = [];
21
+ for (const target of targets) {
22
+ const fullPath = path.join(cwd, target);
23
+ if (fs.existsSync(fullPath) && !force) {
24
+ skipped.push(target);
25
+ continue;
26
+ }
27
+ fs.writeFileSync(fullPath, content);
28
+ written.push(target);
29
+ }
30
+
31
+ return { written, skipped };
32
+ }
33
+
34
+ module.exports = { run };
package/src/render.js ADDED
@@ -0,0 +1,100 @@
1
+ 'use strict';
2
+
3
+ const STRUCTURE_HINTS = {
4
+ 'src/': 'source code',
5
+ 'lib/': 'library code',
6
+ 'test/': 'tests',
7
+ 'tests/': 'tests',
8
+ '__tests__/': 'tests',
9
+ 'spec/': 'tests (spec)',
10
+ 'docs/': 'documentation',
11
+ 'examples/': 'usage examples',
12
+ 'scripts/': 'dev/build scripts',
13
+ 'public/': 'static assets',
14
+ 'assets/': 'static assets',
15
+ '.github/': 'GitHub config (CI, templates)',
16
+ 'bin/': 'CLI entry points',
17
+ 'cmd/': 'Go command entry points',
18
+ 'pkg/': 'Go packages',
19
+ 'internal/': 'Go internal packages',
20
+ };
21
+
22
+ function renderCommands(stack) {
23
+ const c = stack.commands || {};
24
+ const lines = [];
25
+ if (c.install) lines.push(`- Install: \`${c.install}\``);
26
+ if (c.dev) lines.push(`- Dev: \`${c.dev}\``);
27
+ if (c.build) lines.push(`- Build: \`${c.build}\``);
28
+ if (c.test) lines.push(`- Test: \`${c.test}\``);
29
+ if (c.lint) lines.push(`- Lint: \`${c.lint}\``);
30
+ return lines;
31
+ }
32
+
33
+ function renderStack(stack) {
34
+ const parts = [`## ${stack.ecosystem}`];
35
+ const meta = [];
36
+ if (stack.name) meta.push(`**${stack.name}**`);
37
+ if (stack.frameworks && stack.frameworks.length) meta.push(stack.frameworks.join(', '));
38
+ if (stack.packageManager) meta.push(`package manager: ${stack.packageManager}`);
39
+ if (meta.length) parts.push(meta.join(' — '));
40
+
41
+ const cmds = renderCommands(stack);
42
+ if (cmds.length) {
43
+ parts.push('');
44
+ parts.push('### Commands');
45
+ parts.push('');
46
+ parts.push(...cmds);
47
+ } else {
48
+ parts.push('');
49
+ parts.push('_No commands auto-detected — fill these in by hand._');
50
+ }
51
+ return parts.join('\n');
52
+ }
53
+
54
+ function renderStructure(structure) {
55
+ if (!structure.length) return null;
56
+ const lines = structure.map((entry) => {
57
+ const hint = STRUCTURE_HINTS[entry];
58
+ return hint ? `- \`${entry}\` — ${hint}` : `- \`${entry}\``;
59
+ });
60
+ return ['## Structure', '', ...lines].join('\n');
61
+ }
62
+
63
+ function renderNotes(project) {
64
+ const lines = [];
65
+ if (project.ci) {
66
+ lines.push(`- CI: ${project.ci.provider} (${project.ci.workflows.join(', ')})`);
67
+ }
68
+ if (project.license) {
69
+ lines.push(`- License: ${project.license}`);
70
+ }
71
+ if (!lines.length) return null;
72
+ return ['## Notes', '', ...lines].join('\n');
73
+ }
74
+
75
+ function renderAgentsMd(project) {
76
+ const sections = [
77
+ '# AGENTS.md',
78
+ '',
79
+ '> Generated by [agents-md-gen](https://www.npmjs.com/package/agents-md-gen) — a starting point, edit freely.',
80
+ ];
81
+
82
+ if (!project.stacks.length) {
83
+ sections.push('', "Couldn't auto-detect a specific language/framework in this repo.", 'Fill in setup, build, test and lint commands by hand below.');
84
+ } else {
85
+ for (const stack of project.stacks) {
86
+ sections.push('', renderStack(stack));
87
+ }
88
+ }
89
+
90
+ const structure = renderStructure(project.structure);
91
+ if (structure) sections.push('', structure);
92
+
93
+ const notes = renderNotes(project);
94
+ if (notes) sections.push('', notes);
95
+
96
+ sections.push('');
97
+ return sections.join('\n');
98
+ }
99
+
100
+ module.exports = { renderAgentsMd };