genat-mcp 2.2.4 → 2.2.5
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/detect-framework.js +91 -7
- package/index.js +1 -1
- package/package.json +1 -1
package/detect-framework.js
CHANGED
|
@@ -18,7 +18,7 @@ export function detectFramework(parentProjectFolder) {
|
|
|
18
18
|
scriptType: 'typescript',
|
|
19
19
|
bddFramework: 'none',
|
|
20
20
|
pageObject: false,
|
|
21
|
-
projectSummary:
|
|
21
|
+
projectSummary: `Folder not found or empty; using defaults. (resolved: ${root})`,
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -44,8 +44,18 @@ export function detectFramework(parentProjectFolder) {
|
|
|
44
44
|
const hasTs = hasExtension(root, '.ts') || hasExtension(root, '.spec.ts');
|
|
45
45
|
const hasJs = hasExtension(root, '.js') || hasExtension(root, '.spec.js') || hasExtension(root, '.jsx') || hasExtension(root, '.spec.jsx');
|
|
46
46
|
const hasPy = hasExtension(root, '.py') || hasExtension(root, '_test.py');
|
|
47
|
+
const hasConftest = walk(root, (f) => f === 'conftest.py').length > 0;
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
// When both package.json and pyproject.toml exist, prefer Python if pyproject indicates Python project
|
|
50
|
+
let pyprojectIndicatesPython = false;
|
|
51
|
+
if (hasPyproject) {
|
|
52
|
+
try {
|
|
53
|
+
const pyproject = readFileSync(join(root, 'pyproject.toml'), 'utf8');
|
|
54
|
+
pyprojectIndicatesPython = /\[project\]/.test(pyproject) && (/pytest/i.test(pyproject) || /pytest-bdd/i.test(pyproject));
|
|
55
|
+
} catch (_) {}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if ((hasPy || hasConftest || pyprojectIndicatesPython) && (hasRequirements || hasPyproject || hasPy || hasConftest)) {
|
|
49
59
|
scriptType = 'python';
|
|
50
60
|
summaryParts.push('Language: Python');
|
|
51
61
|
} else if (hasPackageJson || hasTsConfig || hasTs || hasJs) {
|
|
@@ -85,9 +95,38 @@ export function detectFramework(parentProjectFolder) {
|
|
|
85
95
|
summaryParts.push('BDD: Behave');
|
|
86
96
|
}
|
|
87
97
|
}
|
|
98
|
+
if (bddFramework === 'none' && hasPyproject) {
|
|
99
|
+
try {
|
|
100
|
+
const pyproject = readFileSync(join(root, 'pyproject.toml'), 'utf8');
|
|
101
|
+
if (/pytest-bdd/i.test(pyproject)) {
|
|
102
|
+
bddFramework = 'pytest-bdd';
|
|
103
|
+
summaryParts.push('BDD: pytest-bdd (pyproject.toml)');
|
|
104
|
+
} else if (/behave/i.test(pyproject)) {
|
|
105
|
+
bddFramework = 'behave';
|
|
106
|
+
summaryParts.push('BDD: Behave (pyproject.toml)');
|
|
107
|
+
}
|
|
108
|
+
} catch (_) {}
|
|
109
|
+
}
|
|
88
110
|
if (bddFramework === 'none' && existsSync(join(root, 'features'))) {
|
|
89
|
-
|
|
90
|
-
|
|
111
|
+
// features/ exists: check .py files for pytest_bdd before defaulting to behave
|
|
112
|
+
const pyFilePaths = walkWithPaths(root, (f) => f.endsWith('.py'));
|
|
113
|
+
let foundPytestBdd = false;
|
|
114
|
+
for (const full of pyFilePaths) {
|
|
115
|
+
try {
|
|
116
|
+
const content = readFileSync(full, 'utf8');
|
|
117
|
+
if (/pytest_bdd|pytest-bdd|from pytest_bdd|import pytest_bdd/i.test(content)) {
|
|
118
|
+
foundPytestBdd = true;
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
} catch (_) {}
|
|
122
|
+
}
|
|
123
|
+
if (foundPytestBdd) {
|
|
124
|
+
bddFramework = 'pytest-bdd';
|
|
125
|
+
summaryParts.push('BDD: pytest-bdd (found in .py files)');
|
|
126
|
+
} else {
|
|
127
|
+
bddFramework = 'behave';
|
|
128
|
+
summaryParts.push('BDD: Behave (features/ present)');
|
|
129
|
+
}
|
|
91
130
|
}
|
|
92
131
|
if (bddFramework === 'none' && hasFeatureFiles) summaryParts.push('Found .feature files');
|
|
93
132
|
} catch (_) {}
|
|
@@ -96,15 +135,33 @@ export function detectFramework(parentProjectFolder) {
|
|
|
96
135
|
summaryParts.push('Found .feature files (consider Cucumber)');
|
|
97
136
|
}
|
|
98
137
|
|
|
99
|
-
// Page Object: pages
|
|
138
|
+
// Page Object: pages/, page_objects/, or *Page.ts, *Page.js, *_page.py, *page.py
|
|
100
139
|
try {
|
|
140
|
+
const hasPagesInSubdir = (function findPagesDir(dir) {
|
|
141
|
+
try {
|
|
142
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
143
|
+
for (const e of entries) {
|
|
144
|
+
if (e.isDirectory() && (e.name === 'pages' || e.name === 'page_objects')) return true;
|
|
145
|
+
if (e.isDirectory() && !['node_modules', '.git', '__pycache__', '.venv', 'venv'].includes(e.name)) {
|
|
146
|
+
if (findPagesDir(join(dir, e.name))) return true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
} catch (_) {}
|
|
150
|
+
return false;
|
|
151
|
+
})(root);
|
|
101
152
|
if (existsSync(join(root, 'pages'))) {
|
|
102
153
|
pageObject = true;
|
|
103
154
|
summaryParts.push('Page Object: pages/ directory');
|
|
155
|
+
} else if (existsSync(join(root, 'page_objects'))) {
|
|
156
|
+
pageObject = true;
|
|
157
|
+
summaryParts.push('Page Object: page_objects/ directory');
|
|
158
|
+
} else if (hasPagesInSubdir) {
|
|
159
|
+
pageObject = true;
|
|
160
|
+
summaryParts.push('Page Object: pages/ or page_objects/ in subdir');
|
|
104
161
|
} else {
|
|
105
162
|
const pageTs = walk(root, (f) => f.endsWith('Page.ts') || f.endsWith('Page.tsx'));
|
|
106
163
|
const pageJs = walk(root, (f) => f.endsWith('Page.js') || f.endsWith('Page.jsx'));
|
|
107
|
-
const pagePy = walk(root, (f) => f.endsWith('_page.py') || f.endsWith('Page.py'));
|
|
164
|
+
const pagePy = walk(root, (f) => f.endsWith('_page.py') || f.endsWith('Page.py') || f.endsWith('page.py'));
|
|
108
165
|
if (pageTs.length || pageJs.length || pagePy.length) {
|
|
109
166
|
pageObject = true;
|
|
110
167
|
summaryParts.push('Page Object: *Page.ts / *Page.js / *_page.py files');
|
|
@@ -112,7 +169,10 @@ export function detectFramework(parentProjectFolder) {
|
|
|
112
169
|
}
|
|
113
170
|
} catch (_) {}
|
|
114
171
|
|
|
115
|
-
const projectSummary =
|
|
172
|
+
const projectSummary = [
|
|
173
|
+
...(summaryParts.length ? summaryParts : ['No framework hints detected']),
|
|
174
|
+
`(scanned: ${root})`,
|
|
175
|
+
].join('; ');
|
|
116
176
|
|
|
117
177
|
return {
|
|
118
178
|
scriptType,
|
|
@@ -122,6 +182,30 @@ export function detectFramework(parentProjectFolder) {
|
|
|
122
182
|
};
|
|
123
183
|
}
|
|
124
184
|
|
|
185
|
+
/**
|
|
186
|
+
* Walk directory and return full paths of files matching predicate.
|
|
187
|
+
* @param {string} dir
|
|
188
|
+
* @param {(f: string) => boolean} predicate
|
|
189
|
+
* @param {string[]} [acc]
|
|
190
|
+
* @returns {string[]}
|
|
191
|
+
*/
|
|
192
|
+
function walkWithPaths(dir, predicate, acc = []) {
|
|
193
|
+
try {
|
|
194
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
195
|
+
for (const e of entries) {
|
|
196
|
+
const full = join(dir, e.name);
|
|
197
|
+
if (e.isDirectory()) {
|
|
198
|
+
if (e.name !== 'node_modules' && e.name !== '.git' && e.name !== '__pycache__' && e.name !== '.venv' && e.name !== 'venv') {
|
|
199
|
+
walkWithPaths(full, predicate, acc);
|
|
200
|
+
}
|
|
201
|
+
} else if (predicate(e.name)) {
|
|
202
|
+
acc.push(full);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
} catch (_) {}
|
|
206
|
+
return acc;
|
|
207
|
+
}
|
|
208
|
+
|
|
125
209
|
/**
|
|
126
210
|
* @param {string} dir
|
|
127
211
|
* @param {(f: string) => boolean} predicate
|
package/index.js
CHANGED
package/package.json
CHANGED