@mmerterden/dev-toolkit-mcp 2.24.1

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +708 -0
  2. package/LICENSE +21 -0
  3. package/README.md +387 -0
  4. package/index.js +1277 -0
  5. package/package.json +80 -0
  6. package/tools/design-check/content-cardinality.js +204 -0
  7. package/tools/design-check/geometry.js +140 -0
  8. package/tools/design-check/index.js +213 -0
  9. package/tools/design-check/mock-detect.js +213 -0
  10. package/tools/design-check/report.js +590 -0
  11. package/tools/design-check/scan.js +91 -0
  12. package/tools/design-check/scenario-inventory.js +598 -0
  13. package/tools/design-check/visual-compare.js +961 -0
  14. package/tools/ios-app-store-audit/context.js +180 -0
  15. package/tools/ios-app-store-audit/data/apple-required-sdks.json +32 -0
  16. package/tools/ios-app-store-audit/data/debug-tools-blocklist.json +133 -0
  17. package/tools/ios-app-store-audit/index.js +164 -0
  18. package/tools/ios-app-store-audit/models.js +47 -0
  19. package/tools/ios-app-store-audit/rules/asset-validation.js +72 -0
  20. package/tools/ios-app-store-audit/rules/binary-size.js +70 -0
  21. package/tools/ios-app-store-audit/rules/code-signing.js +95 -0
  22. package/tools/ios-app-store-audit/rules/dead-reference.js +131 -0
  23. package/tools/ios-app-store-audit/rules/debug-tool-leak.js +185 -0
  24. package/tools/ios-app-store-audit/rules/duplicate-resource.js +130 -0
  25. package/tools/ios-app-store-audit/rules/embedded-sdk.js +126 -0
  26. package/tools/ios-app-store-audit/rules/entitlement.js +105 -0
  27. package/tools/ios-app-store-audit/rules/extension-signing.js +105 -0
  28. package/tools/ios-app-store-audit/rules/info-plist.js +158 -0
  29. package/tools/ios-app-store-audit/rules/ipv6-compliance.js +101 -0
  30. package/tools/ios-app-store-audit/rules/privacy-manifest.js +121 -0
  31. package/tools/ios-app-store-audit/rules/production-hygiene.js +237 -0
  32. package/tools/ios-app-store-audit/rules/provisioning-profile.js +127 -0
  33. package/tools/ios-app-store-audit/rules/required-reason-api.js +123 -0
  34. package/tools/ios-app-store-audit/rules/sdk-floor.js +104 -0
  35. package/tools/ios-app-store-audit/rules/swift-abi.js +64 -0
  36. package/tools/ios-app-store-audit/rules/team-id.js +62 -0
  37. package/tools/ios-testflight/index.js +479 -0
  38. package/ui-tree-dumper.swift +122 -0
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Shared source-scanning primitives for the design-check family.
3
+ *
4
+ * ripgrep when available (fast, .gitignore-aware), bounded node walk otherwise.
5
+ * Both design_mock_detect and design_scenario_inventory scan the same trees, so
6
+ * the globs, skip list, and file budget live here once.
7
+ */
8
+
9
+ import { readdirSync, existsSync } from "fs";
10
+ import { execSync } from "child_process";
11
+ import { join, relative } from "path";
12
+
13
+ export const SKIP_DIRS = new Set([
14
+ "node_modules", "Pods", ".build", "DerivedData", "build", ".git",
15
+ ".gradle", ".worktrees", ".next", "Carthage", "vendor", "__Snapshots__",
16
+ ".idea", ".vscode", "fastlane",
17
+ ]);
18
+ export const SRC_EXT = new Set([".swift", ".kt", ".kts"]);
19
+ export const MAX_FILES = 40000;
20
+ export const RG_GLOBS = ["-g", "*.swift", "-g", "*.kt", "-g", "*.kts",
21
+ "-g", "!Pods/**", "-g", "!**/build/**", "-g", "!**/.build/**", "-g", "!**/DerivedData/**"];
22
+
23
+ let RG_OK = null;
24
+ export function hasRg() {
25
+ if (RG_OK !== null) return RG_OK;
26
+ try { execSync("command -v rg", { stdio: "ignore" }); RG_OK = true; } catch { RG_OK = false; }
27
+ return RG_OK;
28
+ }
29
+
30
+ // Returns array of { file, line, text } (paths absolute), [] on no match.
31
+ export function rg(pattern, path, { multiline = false, files = false, globs = RG_GLOBS } = {}) {
32
+ const base = files
33
+ ? ["--files", ...globs, "-g", pattern]
34
+ : ["-n", "--no-heading", "--no-messages", ...(multiline ? ["-U", "--multiline-dotall"] : []), ...globs, "-e", pattern];
35
+ try {
36
+ const out = execSync(`rg ${base.map((a) => `'${String(a).replace(/'/g, "'\\''")}'`).join(" ")} '${path.replace(/'/g, "'\\''")}'`,
37
+ { encoding: "utf-8", timeout: 45000, maxBuffer: 32 * 1024 * 1024 });
38
+ const lines = out.split("\n").filter(Boolean);
39
+ if (files) return lines.map((f) => ({ file: f, line: 0, text: "" }));
40
+ return lines.map((l) => {
41
+ const m = /^(.*?):(\d+):(.*)$/.exec(l);
42
+ return m ? { file: m[1], line: +m[2], text: m[3] } : null;
43
+ }).filter(Boolean);
44
+ } catch (e) {
45
+ if (e.status === 1) return []; // rg: no matches
46
+ throw e; // rg unusable → caller falls back
47
+ }
48
+ }
49
+
50
+ export function walk(root, onFile, budget = { n: 0 }) {
51
+ let entries;
52
+ try { entries = readdirSync(root, { withFileTypes: true }); } catch { return; }
53
+ for (const e of entries) {
54
+ if (budget.n >= MAX_FILES) return;
55
+ const full = join(root, e.name);
56
+ if (e.isDirectory()) { if (!SKIP_DIRS.has(e.name)) walk(full, onFile, budget); }
57
+ else if (e.isFile()) { budget.n++; onFile(full); }
58
+ }
59
+ }
60
+
61
+ export const rel = (repoPath, f) => { try { return relative(repoPath, f); } catch { return f; } };
62
+
63
+ export function detectPlatform(repoPath) {
64
+ let ios = false, android = false;
65
+ try {
66
+ for (const e of readdirSync(repoPath, { withFileTypes: true })) {
67
+ if (e.name.endsWith(".xcodeproj") || e.name.endsWith(".xcworkspace") || e.name === "Package.swift") ios = true;
68
+ if (/^(build|settings)\.gradle(\.kts)?$/.test(e.name)) android = true;
69
+ }
70
+ } catch { /* ignore */ }
71
+ if (ios && !android) return "ios";
72
+ if (android && !ios) return "android";
73
+ if (hasRg()) {
74
+ try {
75
+ // Probe REAL files: project.pbxproj lives inside every *.xcodeproj (a dir,
76
+ // which `rg --files` never matches); Package.swift is a real file.
77
+ if (rg("**/*.pbxproj", repoPath, { files: true, globs: [] }).length || rg("**/Package.swift", repoPath, { files: true, globs: [] }).length) return "ios";
78
+ if (rg("**/build.gradle", repoPath, { files: true, globs: [] }).length || rg("**/build.gradle.kts", repoPath, { files: true, globs: [] }).length) return "android";
79
+ } catch { /* fall through */ }
80
+ }
81
+ let found = "unknown";
82
+ walk(repoPath, (f) => {
83
+ if (found !== "unknown") return;
84
+ const b = f.split("/").pop();
85
+ if (b === "project.pbxproj" || b === "Package.swift") found = "ios";
86
+ else if (b === "build.gradle" || b === "build.gradle.kts") found = "android";
87
+ });
88
+ return found;
89
+ }
90
+
91
+ export function pathExists(p) { return !!p && existsSync(p); }