typegraph-mcp 0.9.37 → 0.9.39
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/check.ts +55 -16
- package/cli.ts +131 -46
- package/dist/benchmark.js +2 -2
- package/dist/check.js +48 -18
- package/dist/cli.js +419 -97
- package/dist/module-graph.js +4 -3
- package/dist/server.js +261 -43
- package/dist/smoke-test.js +2 -2
- package/export-surface-test.ts +202 -0
- package/install-oxlint-test.ts +95 -0
- package/module-graph.ts +3 -3
- package/package.json +2 -2
- package/server.ts +375 -55
- package/skills/code-exploration/SKILL.md +7 -0
- package/skills/deep-survey/SKILL.md +4 -0
- package/skills/tool-selection/SKILL.md +3 -0
package/check.ts
CHANGED
|
@@ -209,6 +209,44 @@ function hasDeclaredDependency(packageJson: Record<string, unknown> | null, pack
|
|
|
209
209
|
});
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
const ESLINT_CONFIG_NAMES = [
|
|
213
|
+
"eslint.config.mjs",
|
|
214
|
+
"eslint.config.js",
|
|
215
|
+
"eslint.config.ts",
|
|
216
|
+
"eslint.config.cjs",
|
|
217
|
+
];
|
|
218
|
+
const OXLINT_CONFIG_NAMES = [
|
|
219
|
+
".oxlintrc.json",
|
|
220
|
+
"oxlint.config.ts",
|
|
221
|
+
"oxlint.config.js",
|
|
222
|
+
"oxlint.config.mjs",
|
|
223
|
+
"oxlint.config.cjs",
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
type LintConfigCheck =
|
|
227
|
+
| { tool: "ESLint"; fileName: string; fullPath: string; propertyName: "ignores" }
|
|
228
|
+
| { tool: "Oxlint"; fileName: string; fullPath: string; propertyName: "ignorePatterns" };
|
|
229
|
+
|
|
230
|
+
function findLintConfigs(projectRoot: string): LintConfigCheck[] {
|
|
231
|
+
const configs: LintConfigCheck[] = [];
|
|
232
|
+
|
|
233
|
+
for (const fileName of ESLINT_CONFIG_NAMES) {
|
|
234
|
+
const fullPath = path.resolve(projectRoot, fileName);
|
|
235
|
+
if (fs.existsSync(fullPath)) {
|
|
236
|
+
configs.push({ tool: "ESLint", fileName, fullPath, propertyName: "ignores" });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
for (const fileName of OXLINT_CONFIG_NAMES) {
|
|
241
|
+
const fullPath = path.resolve(projectRoot, fileName);
|
|
242
|
+
if (fs.existsSync(fullPath)) {
|
|
243
|
+
configs.push({ tool: "Oxlint", fileName, fullPath, propertyName: "ignorePatterns" });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return configs;
|
|
248
|
+
}
|
|
249
|
+
|
|
212
250
|
// ─── Main ────────────────────────────────────────────────────────────────────
|
|
213
251
|
|
|
214
252
|
export async function main(configOverride?: TypegraphConfig): Promise<CheckResult> {
|
|
@@ -527,31 +565,32 @@ export async function main(configOverride?: TypegraphConfig): Promise<CheckResul
|
|
|
527
565
|
);
|
|
528
566
|
}
|
|
529
567
|
|
|
530
|
-
// 11.
|
|
568
|
+
// 11. Lint ignores (only when typegraph-mcp is embedded inside the project)
|
|
531
569
|
if (toolIsEmbedded) {
|
|
532
|
-
const
|
|
533
|
-
|
|
534
|
-
if (eslintConfigFile) {
|
|
535
|
-
const eslintConfigPath = path.resolve(projectRoot, eslintConfigFile);
|
|
536
|
-
const eslintContent = fs.readFileSync(eslintConfigPath, "utf-8");
|
|
570
|
+
const lintConfigs = findLintConfigs(projectRoot);
|
|
571
|
+
if (lintConfigs.length > 0) {
|
|
537
572
|
// Determine the parent directory (e.g. "plugins") for the ignore pattern
|
|
538
573
|
const parentDir = path.basename(path.dirname(toolDir));
|
|
539
574
|
const parentIgnorePattern = new RegExp(`["']${parentDir}\\/\\*\\*["']`);
|
|
540
|
-
const hasParentIgnore = parentIgnorePattern.test(eslintContent);
|
|
541
575
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
576
|
+
for (const config of lintConfigs) {
|
|
577
|
+
const content = fs.readFileSync(config.fullPath, "utf-8");
|
|
578
|
+
const hasParentIgnore = parentIgnorePattern.test(content);
|
|
579
|
+
|
|
580
|
+
if (hasParentIgnore) {
|
|
581
|
+
pass(`${config.tool} ignores ${parentDir}/ (${config.fileName})`);
|
|
582
|
+
} else {
|
|
583
|
+
fail(
|
|
584
|
+
`${config.tool} missing ignore: "${parentDir}/**" (${config.fileName})`,
|
|
585
|
+
`Add to ${config.propertyName} in ${config.fileName}:\n "${parentDir}/**",`
|
|
586
|
+
);
|
|
587
|
+
}
|
|
549
588
|
}
|
|
550
589
|
} else {
|
|
551
|
-
skip("
|
|
590
|
+
skip("Lint config check (no ESLint or Oxlint config found)");
|
|
552
591
|
}
|
|
553
592
|
} else {
|
|
554
|
-
skip("
|
|
593
|
+
skip("Lint config check (typegraph-mcp is external to project)");
|
|
555
594
|
}
|
|
556
595
|
|
|
557
596
|
// 12. .gitignore check (optional)
|
package/cli.ts
CHANGED
|
@@ -59,6 +59,8 @@ Where suitable, use the \`ts_*\` MCP tools instead of grep/glob for navigating T
|
|
|
59
59
|
|
|
60
60
|
Start with the navigation tools before reading entire files. Use direct file reads only after the MCP tools identify the exact symbols or lines that matter.
|
|
61
61
|
|
|
62
|
+
For quick architectural insight, prefer composition modules and entrypoints over top-level barrel files. If \`ts_module_exports\` on an \`index.ts\` or other barrel looks empty or uninformative, pivot to the app entrypoint, router, handler, service composition root, or API module that wires real behavior together.
|
|
63
|
+
|
|
62
64
|
Use \`rg\` or \`grep\` when semantic symbol navigation is not the right tool, especially for:
|
|
63
65
|
|
|
64
66
|
- docs, config, SQL, migrations, JSON, env vars, route strings, and other non-TypeScript assets
|
|
@@ -599,19 +601,13 @@ function ensureTsconfigExclude(projectRoot: string): void {
|
|
|
599
601
|
|
|
600
602
|
try {
|
|
601
603
|
const raw = fs.readFileSync(tsconfigPath, "utf-8");
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
.replace(/,(\s*[}\]])/g, "$1");
|
|
606
|
-
const tsconfig = JSON.parse(stripped);
|
|
607
|
-
|
|
608
|
-
const exclude: string[] = tsconfig.exclude || [];
|
|
609
|
-
if (exclude.some((e: string) => e === "plugins" || e === "plugins/**" || e === "plugins/*")) {
|
|
610
|
-
return; // Already excluded
|
|
604
|
+
const excludeArrayMatch = raw.match(/("exclude"\s*:\s*\[)([\s\S]*?)(\])/);
|
|
605
|
+
if (excludeArrayMatch && /["']plugins(?:\/\*\*|\/\*|)["']/.test(excludeArrayMatch[2])) {
|
|
606
|
+
return;
|
|
611
607
|
}
|
|
612
608
|
|
|
613
609
|
// Insert "plugins/**" into the exclude array in the original file
|
|
614
|
-
if (
|
|
610
|
+
if (excludeArrayMatch) {
|
|
615
611
|
// Existing exclude array — append to it
|
|
616
612
|
const updated = raw.replace(
|
|
617
613
|
/("exclude"\s*:\s*\[)([\s\S]*?)(\])/,
|
|
@@ -640,48 +636,137 @@ function ensureTsconfigExclude(projectRoot: string): void {
|
|
|
640
636
|
}
|
|
641
637
|
}
|
|
642
638
|
|
|
643
|
-
// ───
|
|
639
|
+
// ─── Lint Ignore ─────────────────────────────────────────────────────────────
|
|
644
640
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
641
|
+
const ESLINT_CONFIG_NAMES = [
|
|
642
|
+
"eslint.config.mjs",
|
|
643
|
+
"eslint.config.js",
|
|
644
|
+
"eslint.config.ts",
|
|
645
|
+
"eslint.config.cjs",
|
|
646
|
+
];
|
|
647
|
+
const OXLINT_CONFIG_NAMES = [
|
|
648
|
+
".oxlintrc.json",
|
|
649
|
+
"oxlint.config.ts",
|
|
650
|
+
"oxlint.config.js",
|
|
651
|
+
"oxlint.config.mjs",
|
|
652
|
+
"oxlint.config.cjs",
|
|
653
|
+
];
|
|
650
654
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
if (pattern.test(raw)) return; // Already ignored
|
|
655
|
+
type LintConfig =
|
|
656
|
+
| { tool: "ESLint"; fileName: string; fullPath: string; format: "flat" }
|
|
657
|
+
| { tool: "Oxlint"; fileName: string; fullPath: string; format: "json" | "module" };
|
|
655
658
|
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
return `${open}${items.trimEnd()}${needsComma ? "," : ""} "plugins/**"${close}`;
|
|
664
|
-
});
|
|
665
|
-
fs.writeFileSync(eslintConfigPath, updated);
|
|
666
|
-
p.log.success(`Added "plugins/**" to ${eslintConfigFile} ignores`);
|
|
667
|
-
return;
|
|
659
|
+
function findLintConfigs(projectRoot: string): LintConfig[] {
|
|
660
|
+
const configs: LintConfig[] = [];
|
|
661
|
+
|
|
662
|
+
for (const fileName of ESLINT_CONFIG_NAMES) {
|
|
663
|
+
const fullPath = path.resolve(projectRoot, fileName);
|
|
664
|
+
if (fs.existsSync(fullPath)) {
|
|
665
|
+
configs.push({ tool: "ESLint", fileName, fullPath, format: "flat" });
|
|
668
666
|
}
|
|
667
|
+
}
|
|
669
668
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
669
|
+
for (const fileName of OXLINT_CONFIG_NAMES) {
|
|
670
|
+
const fullPath = path.resolve(projectRoot, fileName);
|
|
671
|
+
if (fs.existsSync(fullPath)) {
|
|
672
|
+
configs.push({
|
|
673
|
+
tool: "Oxlint",
|
|
674
|
+
fileName,
|
|
675
|
+
fullPath,
|
|
676
|
+
format: fileName.endsWith(".json") ? "json" : "module",
|
|
676
677
|
});
|
|
677
|
-
fs.writeFileSync(eslintConfigPath, updated);
|
|
678
|
-
p.log.success(`Added "plugins/**" to ${eslintConfigFile} ignores`);
|
|
679
|
-
return;
|
|
680
678
|
}
|
|
679
|
+
}
|
|
681
680
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
681
|
+
return configs;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
function appendToArrayLiteral(raw: string, propertyPattern: RegExp, valueLiteral: string): string | null {
|
|
685
|
+
if (!propertyPattern.test(raw)) return null;
|
|
686
|
+
return raw.replace(propertyPattern, (_match, open, items, close) => {
|
|
687
|
+
const trimmed = items.trimEnd();
|
|
688
|
+
const needsComma = trimmed.length > 0 && !trimmed.endsWith(",");
|
|
689
|
+
return `${open}${items.trimEnd()}${needsComma ? "," : ""} ${valueLiteral}${close}`;
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
function insertTopLevelJsonArrayProperty(raw: string, propertyName: string, valueLiteral: string): string | null {
|
|
694
|
+
const lastBrace = raw.lastIndexOf("}");
|
|
695
|
+
if (lastBrace === -1) return null;
|
|
696
|
+
const before = raw.slice(0, lastBrace).trimEnd();
|
|
697
|
+
const needsComma = !before.endsWith(",") && !before.endsWith("{");
|
|
698
|
+
return `${before}${needsComma ? "," : ""}\n "${propertyName}": [${valueLiteral}]\n}\n`;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function patchEslintConfig(raw: string): string | null {
|
|
702
|
+
const updatedIgnores = appendToArrayLiteral(raw, /(ignores\s*:\s*\[)([\s\S]*?)(\])/, '"plugins/**"');
|
|
703
|
+
if (updatedIgnores) return updatedIgnores;
|
|
704
|
+
|
|
705
|
+
// Matches: export default [ or export default tseslint.config(
|
|
706
|
+
const exportArrayRe = /(export\s+default\s+(?:\w+\.config\(|\[))\s*\n?/;
|
|
707
|
+
if (exportArrayRe.test(raw)) {
|
|
708
|
+
return raw.replace(exportArrayRe, (match) => `${match} { ignores: ["plugins/**"] },\n`);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function patchOxlintJsonConfig(raw: string): string | null {
|
|
715
|
+
const updatedIgnores = appendToArrayLiteral(
|
|
716
|
+
raw,
|
|
717
|
+
/("ignorePatterns"\s*:\s*\[)([\s\S]*?)(\])/,
|
|
718
|
+
'"plugins/**"'
|
|
719
|
+
);
|
|
720
|
+
if (updatedIgnores) return updatedIgnores;
|
|
721
|
+
return insertTopLevelJsonArrayProperty(raw, "ignorePatterns", '"plugins/**"');
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function patchOxlintModuleConfig(raw: string): string | null {
|
|
725
|
+
const updatedIgnores = appendToArrayLiteral(
|
|
726
|
+
raw,
|
|
727
|
+
/(ignorePatterns\s*:\s*\[)([\s\S]*?)(\])/,
|
|
728
|
+
'"plugins/**"'
|
|
729
|
+
);
|
|
730
|
+
if (updatedIgnores) return updatedIgnores;
|
|
731
|
+
|
|
732
|
+
const exportObjectRe = /(export\s+default\s*\{)\s*\n?/;
|
|
733
|
+
if (exportObjectRe.test(raw)) {
|
|
734
|
+
return raw.replace(exportObjectRe, (match) => `${match}\n ignorePatterns: ["plugins/**"],`);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
return null;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function ensureLintIgnores(projectRoot: string): void {
|
|
741
|
+
const configs = findLintConfigs(projectRoot);
|
|
742
|
+
for (const config of configs) {
|
|
743
|
+
try {
|
|
744
|
+
const raw = fs.readFileSync(config.fullPath, "utf-8");
|
|
745
|
+
if (/["']plugins\/\*\*["']/.test(raw)) continue;
|
|
746
|
+
|
|
747
|
+
const updated =
|
|
748
|
+
config.tool === "ESLint"
|
|
749
|
+
? patchEslintConfig(raw)
|
|
750
|
+
: config.format === "json"
|
|
751
|
+
? patchOxlintJsonConfig(raw)
|
|
752
|
+
: patchOxlintModuleConfig(raw);
|
|
753
|
+
|
|
754
|
+
if (updated) {
|
|
755
|
+
fs.writeFileSync(config.fullPath, updated);
|
|
756
|
+
const propertyName = config.tool === "ESLint" ? "ignores" : "ignorePatterns";
|
|
757
|
+
p.log.success(`Added "plugins/**" to ${config.fileName} ${propertyName}`);
|
|
758
|
+
} else {
|
|
759
|
+
const propertyName = config.tool === "ESLint" ? "ignores" : "ignorePatterns";
|
|
760
|
+
p.log.warn(
|
|
761
|
+
`Could not patch ${config.fileName} — manually add "plugins/**" to ${propertyName}`
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
} catch {
|
|
765
|
+
const propertyName = config.tool === "ESLint" ? "ignores" : "ignorePatterns";
|
|
766
|
+
p.log.warn(
|
|
767
|
+
`Could not update ${config.fileName} — manually add "plugins/**" to ${propertyName}`
|
|
768
|
+
);
|
|
769
|
+
}
|
|
685
770
|
}
|
|
686
771
|
}
|
|
687
772
|
|
|
@@ -918,8 +1003,8 @@ async function setup(yes: boolean): Promise<void> {
|
|
|
918
1003
|
// 8. Ensure plugins/ is excluded from tsconfig
|
|
919
1004
|
ensureTsconfigExclude(projectRoot);
|
|
920
1005
|
|
|
921
|
-
// 9. Ensure plugins/ is ignored by
|
|
922
|
-
|
|
1006
|
+
// 9. Ensure plugins/ is ignored by supported lint configs
|
|
1007
|
+
ensureLintIgnores(projectRoot);
|
|
923
1008
|
|
|
924
1009
|
// 10. Verification
|
|
925
1010
|
await runVerification(targetDir, selectedAgents);
|
package/dist/benchmark.js
CHANGED
|
@@ -387,7 +387,7 @@ function distToSource(resolvedPath, projectRoot2) {
|
|
|
387
387
|
}
|
|
388
388
|
return resolvedPath;
|
|
389
389
|
}
|
|
390
|
-
function
|
|
390
|
+
function resolveProjectImport(resolver, fromDir, specifier, projectRoot2) {
|
|
391
391
|
try {
|
|
392
392
|
const result = resolver.sync(fromDir, specifier);
|
|
393
393
|
if (result.path && !result.path.includes("node_modules")) {
|
|
@@ -438,7 +438,7 @@ function buildForwardEdges(files, resolver, projectRoot2) {
|
|
|
438
438
|
const edges = [];
|
|
439
439
|
const fromDir = path2.dirname(filePath);
|
|
440
440
|
for (const raw of rawImports) {
|
|
441
|
-
const target =
|
|
441
|
+
const target = resolveProjectImport(resolver, fromDir, raw.specifier, projectRoot2);
|
|
442
442
|
if (target) {
|
|
443
443
|
edges.push({
|
|
444
444
|
target,
|
package/dist/check.js
CHANGED
|
@@ -15,6 +15,7 @@ __export(module_graph_exports, {
|
|
|
15
15
|
createResolver: () => createResolver,
|
|
16
16
|
discoverFiles: () => discoverFiles,
|
|
17
17
|
removeFile: () => removeFile,
|
|
18
|
+
resolveProjectImport: () => resolveProjectImport,
|
|
18
19
|
startWatcher: () => startWatcher,
|
|
19
20
|
updateFile: () => updateFile
|
|
20
21
|
});
|
|
@@ -120,7 +121,7 @@ function distToSource(resolvedPath, projectRoot) {
|
|
|
120
121
|
}
|
|
121
122
|
return resolvedPath;
|
|
122
123
|
}
|
|
123
|
-
function
|
|
124
|
+
function resolveProjectImport(resolver, fromDir, specifier, projectRoot) {
|
|
124
125
|
try {
|
|
125
126
|
const result = resolver.sync(fromDir, specifier);
|
|
126
127
|
if (result.path && !result.path.includes("node_modules")) {
|
|
@@ -171,7 +172,7 @@ function buildForwardEdges(files, resolver, projectRoot) {
|
|
|
171
172
|
const edges = [];
|
|
172
173
|
const fromDir = path2.dirname(filePath);
|
|
173
174
|
for (const raw of rawImports) {
|
|
174
|
-
const target =
|
|
175
|
+
const target = resolveProjectImport(resolver, fromDir, raw.specifier, projectRoot);
|
|
175
176
|
if (target) {
|
|
176
177
|
edges.push({
|
|
177
178
|
target,
|
|
@@ -252,7 +253,7 @@ function updateFile(graph, filePath, resolver, projectRoot) {
|
|
|
252
253
|
const fromDir = path2.dirname(filePath);
|
|
253
254
|
const newEdges = [];
|
|
254
255
|
for (const raw of rawImports) {
|
|
255
|
-
const target =
|
|
256
|
+
const target = resolveProjectImport(resolver, fromDir, raw.specifier, projectRoot);
|
|
256
257
|
if (target) {
|
|
257
258
|
newEdges.push({
|
|
258
259
|
target,
|
|
@@ -506,6 +507,35 @@ function hasDeclaredDependency(packageJson, packageName) {
|
|
|
506
507
|
return typeof deps === "object" && deps !== null && packageName in deps;
|
|
507
508
|
});
|
|
508
509
|
}
|
|
510
|
+
var ESLINT_CONFIG_NAMES = [
|
|
511
|
+
"eslint.config.mjs",
|
|
512
|
+
"eslint.config.js",
|
|
513
|
+
"eslint.config.ts",
|
|
514
|
+
"eslint.config.cjs"
|
|
515
|
+
];
|
|
516
|
+
var OXLINT_CONFIG_NAMES = [
|
|
517
|
+
".oxlintrc.json",
|
|
518
|
+
"oxlint.config.ts",
|
|
519
|
+
"oxlint.config.js",
|
|
520
|
+
"oxlint.config.mjs",
|
|
521
|
+
"oxlint.config.cjs"
|
|
522
|
+
];
|
|
523
|
+
function findLintConfigs(projectRoot) {
|
|
524
|
+
const configs = [];
|
|
525
|
+
for (const fileName of ESLINT_CONFIG_NAMES) {
|
|
526
|
+
const fullPath = path3.resolve(projectRoot, fileName);
|
|
527
|
+
if (fs2.existsSync(fullPath)) {
|
|
528
|
+
configs.push({ tool: "ESLint", fileName, fullPath, propertyName: "ignores" });
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
for (const fileName of OXLINT_CONFIG_NAMES) {
|
|
532
|
+
const fullPath = path3.resolve(projectRoot, fileName);
|
|
533
|
+
if (fs2.existsSync(fullPath)) {
|
|
534
|
+
configs.push({ tool: "Oxlint", fileName, fullPath, propertyName: "ignorePatterns" });
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return configs;
|
|
538
|
+
}
|
|
509
539
|
async function main(configOverride) {
|
|
510
540
|
const { projectRoot, tsconfigPath, toolDir, toolIsEmbedded, toolRelPath } = configOverride ?? resolveConfig(import.meta.dirname);
|
|
511
541
|
let passed = 0;
|
|
@@ -783,28 +813,28 @@ async function main(configOverride) {
|
|
|
783
813
|
);
|
|
784
814
|
}
|
|
785
815
|
if (toolIsEmbedded) {
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
if (eslintConfigFile) {
|
|
789
|
-
const eslintConfigPath = path3.resolve(projectRoot, eslintConfigFile);
|
|
790
|
-
const eslintContent = fs2.readFileSync(eslintConfigPath, "utf-8");
|
|
816
|
+
const lintConfigs = findLintConfigs(projectRoot);
|
|
817
|
+
if (lintConfigs.length > 0) {
|
|
791
818
|
const parentDir = path3.basename(path3.dirname(toolDir));
|
|
792
819
|
const parentIgnorePattern = new RegExp(`["']${parentDir}\\/\\*\\*["']`);
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
820
|
+
for (const config of lintConfigs) {
|
|
821
|
+
const content = fs2.readFileSync(config.fullPath, "utf-8");
|
|
822
|
+
const hasParentIgnore = parentIgnorePattern.test(content);
|
|
823
|
+
if (hasParentIgnore) {
|
|
824
|
+
pass(`${config.tool} ignores ${parentDir}/ (${config.fileName})`);
|
|
825
|
+
} else {
|
|
826
|
+
fail(
|
|
827
|
+
`${config.tool} missing ignore: "${parentDir}/**" (${config.fileName})`,
|
|
828
|
+
`Add to ${config.propertyName} in ${config.fileName}:
|
|
800
829
|
"${parentDir}/**",`
|
|
801
|
-
|
|
830
|
+
);
|
|
831
|
+
}
|
|
802
832
|
}
|
|
803
833
|
} else {
|
|
804
|
-
skip("
|
|
834
|
+
skip("Lint config check (no ESLint or Oxlint config found)");
|
|
805
835
|
}
|
|
806
836
|
} else {
|
|
807
|
-
skip("
|
|
837
|
+
skip("Lint config check (typegraph-mcp is external to project)");
|
|
808
838
|
}
|
|
809
839
|
const gitignorePath = path3.resolve(projectRoot, ".gitignore");
|
|
810
840
|
if (fs2.existsSync(gitignorePath)) {
|