@kb-labs/devkit 1.0.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.
Files changed (295) hide show
  1. package/.cursorrules +32 -0
  2. package/.github/CODEOWNERS +2 -0
  3. package/.github/actions/setup-node-pnpm/action.yml +47 -0
  4. package/.github/workflow-templates/ci.yml +13 -0
  5. package/.github/workflow-templates/drift-check.yml +10 -0
  6. package/.github/workflow-templates/profiles-validate.yml +16 -0
  7. package/.github/workflow-templates/release.yml +8 -0
  8. package/.github/workflows/ci-reusable.yml +131 -0
  9. package/.github/workflows/drift-check-reusable.yml +23 -0
  10. package/.github/workflows/fixtures.yml +74 -0
  11. package/.github/workflows/profiles-validate-reusable.yml +67 -0
  12. package/.github/workflows/release-reusable.yml +50 -0
  13. package/.vscode/settings.json +23 -0
  14. package/AGENTS.md +130 -0
  15. package/LICENSE +21 -0
  16. package/README.md +1542 -0
  17. package/agents/devkit-maintainer/context.globs +15 -0
  18. package/agents/devkit-maintainer/permissions.yml +17 -0
  19. package/agents/devkit-maintainer/prompt.md +28 -0
  20. package/agents/devkit-maintainer/runbook.md +31 -0
  21. package/agents/docs-crafter/prompt.md +24 -0
  22. package/agents/docs-crafter/runbook.md +18 -0
  23. package/agents/release-manager/context.globs +7 -0
  24. package/agents/release-manager/prompt.md +27 -0
  25. package/agents/release-manager/runbook.md +17 -0
  26. package/agents/test-generator/context.globs +7 -0
  27. package/agents/test-generator/prompt.md +27 -0
  28. package/agents/test-generator/runbook.md +18 -0
  29. package/bin/devkit-architecture.mjs +1225 -0
  30. package/bin/devkit-build-order.mjs +500 -0
  31. package/bin/devkit-check-build-readiness.mjs +222 -0
  32. package/bin/devkit-check-commands.mjs +394 -0
  33. package/bin/devkit-check-configs.mjs +461 -0
  34. package/bin/devkit-check-deprecated.mjs +532 -0
  35. package/bin/devkit-check-duplicates.mjs +431 -0
  36. package/bin/devkit-check-exports.mjs +561 -0
  37. package/bin/devkit-check-imports.mjs +712 -0
  38. package/bin/devkit-check-paths.mjs +670 -0
  39. package/bin/devkit-check-scripts.mjs +335 -0
  40. package/bin/devkit-check-structure.mjs +495 -0
  41. package/bin/devkit-check-types.mjs +450 -0
  42. package/bin/devkit-ci.mjs +261 -0
  43. package/bin/devkit-core-gate.mjs +225 -0
  44. package/bin/devkit-fix-deps.mjs +1169 -0
  45. package/bin/devkit-freshness.mjs +199 -0
  46. package/bin/devkit-health.mjs +489 -0
  47. package/bin/devkit-migrate-configs.mjs +370 -0
  48. package/bin/devkit-paths.mjs +192 -0
  49. package/bin/devkit-stats.mjs +453 -0
  50. package/bin/devkit-sync.mjs +12 -0
  51. package/bin/devkit-tsup-external.mjs +148 -0
  52. package/bin/devkit-types-audit.mjs +615 -0
  53. package/bin/devkit-types-order.mjs +637 -0
  54. package/bin/devkit-validate-naming.mjs +203 -0
  55. package/bin/devkit-visualize.mjs +452 -0
  56. package/bin/kb-devkit-qa-history.mjs +453 -0
  57. package/bin/kb-devkit-qa.mjs +915 -0
  58. package/eslint/node.js +134 -0
  59. package/eslint/react.js +260 -0
  60. package/package.json +186 -0
  61. package/prettier/index.json +10 -0
  62. package/sync/index.mjs +693 -0
  63. package/templates/configs/README.md +306 -0
  64. package/templates/configs/eslint.config.js +27 -0
  65. package/templates/configs/package.json.bin +25 -0
  66. package/templates/configs/package.json.lib +30 -0
  67. package/templates/configs/tsconfig.build.json +15 -0
  68. package/templates/configs/tsconfig.json +9 -0
  69. package/templates/configs/tsup.config.bin.ts +34 -0
  70. package/templates/configs/tsup.config.cli.ts +41 -0
  71. package/templates/configs/tsup.config.dual.ts +46 -0
  72. package/templates/configs/tsup.config.ts +36 -0
  73. package/templates/package-json/README.md +290 -0
  74. package/templates/package-json/package.json.bin.template +40 -0
  75. package/templates/package-json/package.json.template +45 -0
  76. package/tsconfig/base.json +21 -0
  77. package/tsconfig/cli.json +12 -0
  78. package/tsconfig/dist/__tests__/cache.spec.d.ts +6 -0
  79. package/tsconfig/dist/__tests__/cache.spec.d.ts.map +1 -0
  80. package/tsconfig/dist/__tests__/cache.spec.js +85 -0
  81. package/tsconfig/dist/__tests__/cache.spec.js.map +1 -0
  82. package/tsconfig/dist/__tests__/fs-atomic.spec.d.ts +6 -0
  83. package/tsconfig/dist/__tests__/fs-atomic.spec.d.ts.map +1 -0
  84. package/tsconfig/dist/__tests__/fs-atomic.spec.js +153 -0
  85. package/tsconfig/dist/__tests__/fs-atomic.spec.js.map +1 -0
  86. package/tsconfig/dist/__tests__/init-workspace.spec.d.ts +6 -0
  87. package/tsconfig/dist/__tests__/init-workspace.spec.d.ts.map +1 -0
  88. package/tsconfig/dist/__tests__/init-workspace.spec.js +99 -0
  89. package/tsconfig/dist/__tests__/init-workspace.spec.js.map +1 -0
  90. package/tsconfig/dist/__tests__/kb-error.spec.d.ts +6 -0
  91. package/tsconfig/dist/__tests__/kb-error.spec.d.ts.map +1 -0
  92. package/tsconfig/dist/__tests__/kb-error.spec.js +190 -0
  93. package/tsconfig/dist/__tests__/kb-error.spec.js.map +1 -0
  94. package/tsconfig/dist/__tests__/preset-lockfile.spec.d.ts +6 -0
  95. package/tsconfig/dist/__tests__/preset-lockfile.spec.d.ts.map +1 -0
  96. package/tsconfig/dist/__tests__/preset-lockfile.spec.js +142 -0
  97. package/tsconfig/dist/__tests__/preset-lockfile.spec.js.map +1 -0
  98. package/tsconfig/dist/__tests__/product-config-profiles.spec.d.ts +6 -0
  99. package/tsconfig/dist/__tests__/product-config-profiles.spec.d.ts.map +1 -0
  100. package/tsconfig/dist/__tests__/product-config-profiles.spec.js +100 -0
  101. package/tsconfig/dist/__tests__/product-config-profiles.spec.js.map +1 -0
  102. package/tsconfig/dist/__tests__/product-config.spec.d.ts +6 -0
  103. package/tsconfig/dist/__tests__/product-config.spec.d.ts.map +1 -0
  104. package/tsconfig/dist/__tests__/product-config.spec.js +298 -0
  105. package/tsconfig/dist/__tests__/product-config.spec.js.map +1 -0
  106. package/tsconfig/dist/__tests__/runtime.spec.d.ts +2 -0
  107. package/tsconfig/dist/__tests__/runtime.spec.d.ts.map +1 -0
  108. package/tsconfig/dist/__tests__/runtime.spec.js +127 -0
  109. package/tsconfig/dist/__tests__/runtime.spec.js.map +1 -0
  110. package/tsconfig/dist/__tests__/upsert-lockfile.spec.d.ts +6 -0
  111. package/tsconfig/dist/__tests__/upsert-lockfile.spec.d.ts.map +1 -0
  112. package/tsconfig/dist/__tests__/upsert-lockfile.spec.js +251 -0
  113. package/tsconfig/dist/__tests__/upsert-lockfile.spec.js.map +1 -0
  114. package/tsconfig/dist/__tests__/validate-config.spec.d.ts +2 -0
  115. package/tsconfig/dist/__tests__/validate-config.spec.d.ts.map +1 -0
  116. package/tsconfig/dist/__tests__/validate-config.spec.js +14 -0
  117. package/tsconfig/dist/__tests__/validate-config.spec.js.map +1 -0
  118. package/tsconfig/dist/api/init-workspace.d.ts +10 -0
  119. package/tsconfig/dist/api/init-workspace.d.ts.map +1 -0
  120. package/tsconfig/dist/api/init-workspace.js +191 -0
  121. package/tsconfig/dist/api/init-workspace.js.map +1 -0
  122. package/tsconfig/dist/api/product-config.d.ts +21 -0
  123. package/tsconfig/dist/api/product-config.d.ts.map +1 -0
  124. package/tsconfig/dist/api/product-config.js +192 -0
  125. package/tsconfig/dist/api/product-config.js.map +1 -0
  126. package/tsconfig/dist/api/read-config.d.ts +22 -0
  127. package/tsconfig/dist/api/read-config.d.ts.map +1 -0
  128. package/tsconfig/dist/api/read-config.js +105 -0
  129. package/tsconfig/dist/api/read-config.js.map +1 -0
  130. package/tsconfig/dist/api/upsert-lockfile.d.ts +10 -0
  131. package/tsconfig/dist/api/upsert-lockfile.d.ts.map +1 -0
  132. package/tsconfig/dist/api/upsert-lockfile.js +63 -0
  133. package/tsconfig/dist/api/upsert-lockfile.js.map +1 -0
  134. package/tsconfig/dist/cache/fs-cache.d.ts +38 -0
  135. package/tsconfig/dist/cache/fs-cache.d.ts.map +1 -0
  136. package/tsconfig/dist/cache/fs-cache.js +142 -0
  137. package/tsconfig/dist/cache/fs-cache.js.map +1 -0
  138. package/tsconfig/dist/errors/kb-error.d.ts +32 -0
  139. package/tsconfig/dist/errors/kb-error.d.ts.map +1 -0
  140. package/tsconfig/dist/errors/kb-error.js +54 -0
  141. package/tsconfig/dist/errors/kb-error.js.map +1 -0
  142. package/tsconfig/dist/fs/__tests__/fs.spec.d.ts +2 -0
  143. package/tsconfig/dist/fs/__tests__/fs.spec.d.ts.map +1 -0
  144. package/tsconfig/dist/fs/__tests__/fs.spec.js +22 -0
  145. package/tsconfig/dist/fs/__tests__/fs.spec.js.map +1 -0
  146. package/tsconfig/dist/fs/fs.d.ts +6 -0
  147. package/tsconfig/dist/fs/fs.d.ts.map +1 -0
  148. package/tsconfig/dist/fs/fs.js +12 -0
  149. package/tsconfig/dist/fs/fs.js.map +1 -0
  150. package/tsconfig/dist/fs/index.d.ts +2 -0
  151. package/tsconfig/dist/fs/index.d.ts.map +1 -0
  152. package/tsconfig/dist/fs/index.js +2 -0
  153. package/tsconfig/dist/fs/index.js.map +1 -0
  154. package/tsconfig/dist/hash/config-hash.d.ts +17 -0
  155. package/tsconfig/dist/hash/config-hash.d.ts.map +1 -0
  156. package/tsconfig/dist/hash/config-hash.js +55 -0
  157. package/tsconfig/dist/hash/config-hash.js.map +1 -0
  158. package/tsconfig/dist/index.d.ts +5 -0
  159. package/tsconfig/dist/index.d.ts.map +1 -0
  160. package/tsconfig/dist/index.js +5 -0
  161. package/tsconfig/dist/index.js.map +1 -0
  162. package/tsconfig/dist/lockfile/lockfile.d.ts +54 -0
  163. package/tsconfig/dist/lockfile/lockfile.d.ts.map +1 -0
  164. package/tsconfig/dist/lockfile/lockfile.js +141 -0
  165. package/tsconfig/dist/lockfile/lockfile.js.map +1 -0
  166. package/tsconfig/dist/logging/__tests__/logger.spec.d.ts +2 -0
  167. package/tsconfig/dist/logging/__tests__/logger.spec.d.ts.map +1 -0
  168. package/tsconfig/dist/logging/__tests__/logger.spec.js +65 -0
  169. package/tsconfig/dist/logging/__tests__/logger.spec.js.map +1 -0
  170. package/tsconfig/dist/logging/__tests__/redaction.spec.d.ts +2 -0
  171. package/tsconfig/dist/logging/__tests__/redaction.spec.d.ts.map +1 -0
  172. package/tsconfig/dist/logging/__tests__/redaction.spec.js +34 -0
  173. package/tsconfig/dist/logging/__tests__/redaction.spec.js.map +1 -0
  174. package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.d.ts +2 -0
  175. package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.d.ts.map +1 -0
  176. package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.js +90 -0
  177. package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.js.map +1 -0
  178. package/tsconfig/dist/logging/index.d.ts +6 -0
  179. package/tsconfig/dist/logging/index.d.ts.map +1 -0
  180. package/tsconfig/dist/logging/index.js +6 -0
  181. package/tsconfig/dist/logging/index.js.map +1 -0
  182. package/tsconfig/dist/logging/logger.d.ts +9 -0
  183. package/tsconfig/dist/logging/logger.d.ts.map +1 -0
  184. package/tsconfig/dist/logging/logger.js +101 -0
  185. package/tsconfig/dist/logging/logger.js.map +1 -0
  186. package/tsconfig/dist/logging/redaction.d.ts +7 -0
  187. package/tsconfig/dist/logging/redaction.d.ts.map +1 -0
  188. package/tsconfig/dist/logging/redaction.js +23 -0
  189. package/tsconfig/dist/logging/redaction.js.map +1 -0
  190. package/tsconfig/dist/logging/sinks/json.d.ts +4 -0
  191. package/tsconfig/dist/logging/sinks/json.d.ts.map +1 -0
  192. package/tsconfig/dist/logging/sinks/json.js +23 -0
  193. package/tsconfig/dist/logging/sinks/json.js.map +1 -0
  194. package/tsconfig/dist/logging/sinks/stdout.d.ts +3 -0
  195. package/tsconfig/dist/logging/sinks/stdout.d.ts.map +1 -0
  196. package/tsconfig/dist/logging/sinks/stdout.js +24 -0
  197. package/tsconfig/dist/logging/sinks/stdout.js.map +1 -0
  198. package/tsconfig/dist/logging/types/index.d.ts +2 -0
  199. package/tsconfig/dist/logging/types/index.d.ts.map +1 -0
  200. package/tsconfig/dist/logging/types/index.js +2 -0
  201. package/tsconfig/dist/logging/types/index.js.map +1 -0
  202. package/tsconfig/dist/logging/types/types.d.ts +37 -0
  203. package/tsconfig/dist/logging/types/types.d.ts.map +1 -0
  204. package/tsconfig/dist/logging/types/types.js +2 -0
  205. package/tsconfig/dist/logging/types/types.js.map +1 -0
  206. package/tsconfig/dist/merge/layered-merge.d.ts +16 -0
  207. package/tsconfig/dist/merge/layered-merge.d.ts.map +1 -0
  208. package/tsconfig/dist/merge/layered-merge.js +97 -0
  209. package/tsconfig/dist/merge/layered-merge.js.map +1 -0
  210. package/tsconfig/dist/preset/resolve-preset.d.ts +29 -0
  211. package/tsconfig/dist/preset/resolve-preset.d.ts.map +1 -0
  212. package/tsconfig/dist/preset/resolve-preset.js +104 -0
  213. package/tsconfig/dist/preset/resolve-preset.js.map +1 -0
  214. package/tsconfig/dist/repo/__tests__/repo.spec.d.ts +2 -0
  215. package/tsconfig/dist/repo/__tests__/repo.spec.d.ts.map +1 -0
  216. package/tsconfig/dist/repo/__tests__/repo.spec.js +25 -0
  217. package/tsconfig/dist/repo/__tests__/repo.spec.js.map +1 -0
  218. package/tsconfig/dist/repo/index.d.ts +2 -0
  219. package/tsconfig/dist/repo/index.d.ts.map +1 -0
  220. package/tsconfig/dist/repo/index.js +2 -0
  221. package/tsconfig/dist/repo/index.js.map +1 -0
  222. package/tsconfig/dist/repo/repo.d.ts +6 -0
  223. package/tsconfig/dist/repo/repo.d.ts.map +1 -0
  224. package/tsconfig/dist/repo/repo.js +25 -0
  225. package/tsconfig/dist/repo/repo.js.map +1 -0
  226. package/tsconfig/dist/runtime/index.d.ts +2 -0
  227. package/tsconfig/dist/runtime/index.d.ts.map +1 -0
  228. package/tsconfig/dist/runtime/index.js +2 -0
  229. package/tsconfig/dist/runtime/index.js.map +1 -0
  230. package/tsconfig/dist/runtime/runtime.d.ts +46 -0
  231. package/tsconfig/dist/runtime/runtime.d.ts.map +1 -0
  232. package/tsconfig/dist/runtime/runtime.js +126 -0
  233. package/tsconfig/dist/runtime/runtime.js.map +1 -0
  234. package/tsconfig/dist/tsconfig.tools.tsbuildinfo +1 -0
  235. package/tsconfig/dist/tsconfig.tsbuildinfo +1 -0
  236. package/tsconfig/dist/types/index.d.ts +2 -0
  237. package/tsconfig/dist/types/index.d.ts.map +1 -0
  238. package/tsconfig/dist/types/index.js +2 -0
  239. package/tsconfig/dist/types/index.js.map +1 -0
  240. package/tsconfig/dist/types/init.d.ts +34 -0
  241. package/tsconfig/dist/types/init.d.ts.map +1 -0
  242. package/tsconfig/dist/types/init.js +6 -0
  243. package/tsconfig/dist/types/init.js.map +1 -0
  244. package/tsconfig/dist/types/preset.d.ts +27 -0
  245. package/tsconfig/dist/types/preset.d.ts.map +1 -0
  246. package/tsconfig/dist/types/preset.js +6 -0
  247. package/tsconfig/dist/types/preset.js.map +1 -0
  248. package/tsconfig/dist/types/types.d.ts +6 -0
  249. package/tsconfig/dist/types/types.d.ts.map +1 -0
  250. package/tsconfig/dist/types/types.js +2 -0
  251. package/tsconfig/dist/types/types.js.map +1 -0
  252. package/tsconfig/dist/utils/__tests__/env.spec.d.ts +2 -0
  253. package/tsconfig/dist/utils/__tests__/env.spec.d.ts.map +1 -0
  254. package/tsconfig/dist/utils/__tests__/env.spec.js +33 -0
  255. package/tsconfig/dist/utils/__tests__/env.spec.js.map +1 -0
  256. package/tsconfig/dist/utils/env.d.ts +7 -0
  257. package/tsconfig/dist/utils/env.d.ts.map +1 -0
  258. package/tsconfig/dist/utils/env.js +25 -0
  259. package/tsconfig/dist/utils/env.js.map +1 -0
  260. package/tsconfig/dist/utils/fs-atomic.d.ts +15 -0
  261. package/tsconfig/dist/utils/fs-atomic.d.ts.map +1 -0
  262. package/tsconfig/dist/utils/fs-atomic.js +45 -0
  263. package/tsconfig/dist/utils/fs-atomic.js.map +1 -0
  264. package/tsconfig/dist/utils/index.d.ts +3 -0
  265. package/tsconfig/dist/utils/index.d.ts.map +1 -0
  266. package/tsconfig/dist/utils/index.js +3 -0
  267. package/tsconfig/dist/utils/index.js.map +1 -0
  268. package/tsconfig/dist/utils/paths.d.ts +21 -0
  269. package/tsconfig/dist/utils/paths.d.ts.map +1 -0
  270. package/tsconfig/dist/utils/paths.js +32 -0
  271. package/tsconfig/dist/utils/paths.js.map +1 -0
  272. package/tsconfig/dist/utils/product-normalize.d.ts +27 -0
  273. package/tsconfig/dist/utils/product-normalize.d.ts.map +1 -0
  274. package/tsconfig/dist/utils/product-normalize.js +45 -0
  275. package/tsconfig/dist/utils/product-normalize.js.map +1 -0
  276. package/tsconfig/dist/validation/validate-config.d.ts +7 -0
  277. package/tsconfig/dist/validation/validate-config.d.ts.map +1 -0
  278. package/tsconfig/dist/validation/validate-config.js +22 -0
  279. package/tsconfig/dist/validation/validate-config.js.map +1 -0
  280. package/tsconfig/lib.json +13 -0
  281. package/tsconfig/node.json +12 -0
  282. package/tsconfig/react-app.json +8 -0
  283. package/tsconfig/react-lib.json +8 -0
  284. package/tsconfig/test.json +15 -0
  285. package/tsup/bin.js +172 -0
  286. package/tsup/dual.js +155 -0
  287. package/tsup/external-sync.mjs +41 -0
  288. package/tsup/external.mjs +109 -0
  289. package/tsup/node.js +65 -0
  290. package/tsup/react-lib.js +18 -0
  291. package/tsup/sdk.js +118 -0
  292. package/vite/react-app.js +50 -0
  293. package/vitest/node.js +37 -0
  294. package/vitest/react.js +39 -0
  295. package/vitest/vitest-setup.ts +9 -0
@@ -0,0 +1,431 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @kb-labs/devkit - Duplicate Checker
5
+ *
6
+ * Checks for:
7
+ * 1. Duplicate dependencies (same package, different versions)
8
+ * 2. Version mismatches across packages
9
+ * 3. Duplicate code patterns (similar file names/content)
10
+ * 4. Redundant similar packages
11
+ *
12
+ * Usage:
13
+ * kb-devkit-check-duplicates # Check all packages
14
+ * kb-devkit-check-duplicates --package cli-core # Check specific package
15
+ * kb-devkit-check-duplicates --code # Include code duplication check
16
+ */
17
+
18
+ import fs from 'node:fs';
19
+ import path from 'node:path';
20
+ import { fileURLToPath } from 'node:url';
21
+
22
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
23
+
24
+ // ANSI colors
25
+ const colors = {
26
+ reset: '\x1b[0m',
27
+ red: '\x1b[31m',
28
+ green: '\x1b[32m',
29
+ yellow: '\x1b[33m',
30
+ blue: '\x1b[34m',
31
+ magenta: '\x1b[35m',
32
+ cyan: '\x1b[36m',
33
+ gray: '\x1b[90m',
34
+ };
35
+
36
+ function log(message, color = 'reset') {
37
+ console.log(`${colors[color]}${message}${colors.reset}`);
38
+ }
39
+
40
+ // Parse CLI arguments
41
+ const args = process.argv.slice(2);
42
+ const options = {
43
+ package: args.find((arg) => arg.startsWith('--package='))?.split('=')[1],
44
+ code: args.includes('--code'),
45
+ verbose: args.includes('--verbose') || args.includes('-v'),
46
+ };
47
+
48
+ /**
49
+ * Find all packages in monorepo
50
+ */
51
+ function findPackages(rootDir, filterPackage) {
52
+ const packages = [];
53
+ const entries = fs.readdirSync(rootDir, { withFileTypes: true });
54
+
55
+ for (const entry of entries) {
56
+ if (!entry.isDirectory() || !entry.name.startsWith('kb-labs-')) {continue;}
57
+
58
+ const repoPath = path.join(rootDir, entry.name);
59
+ const packagesDir = path.join(repoPath, 'packages');
60
+
61
+ if (!fs.existsSync(packagesDir)) {continue;}
62
+
63
+ const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true });
64
+
65
+ for (const pkgDir of packageDirs) {
66
+ if (!pkgDir.isDirectory()) {continue;}
67
+
68
+ // Filter by package name if specified
69
+ if (filterPackage && pkgDir.name !== filterPackage) {continue;}
70
+
71
+ const packageJsonPath = path.join(packagesDir, pkgDir.name, 'package.json');
72
+
73
+ if (fs.existsSync(packageJsonPath)) {
74
+ packages.push(packageJsonPath);
75
+ }
76
+ }
77
+ }
78
+
79
+ return packages;
80
+ }
81
+
82
+ /**
83
+ * Collect all dependencies from all packages
84
+ */
85
+ function collectDependencies(packages) {
86
+ const dependencyMap = new Map(); // dep -> [{package, version}]
87
+
88
+ for (const packagePath of packages) {
89
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
90
+ const packageName = packageJson.name;
91
+
92
+ if (!packageName || !packageName.startsWith('@kb-labs/')) {continue;}
93
+
94
+ const allDeps = {
95
+ ...packageJson.dependencies,
96
+ ...packageJson.devDependencies,
97
+ ...packageJson.peerDependencies,
98
+ };
99
+
100
+ for (const [dep, version] of Object.entries(allDeps)) {
101
+ // Skip workspace packages and local links
102
+ if (version.startsWith('workspace:') || version.startsWith('link:')) {
103
+ continue;
104
+ }
105
+
106
+ if (!dependencyMap.has(dep)) {
107
+ dependencyMap.set(dep, []);
108
+ }
109
+
110
+ dependencyMap.get(dep).push({
111
+ package: packageName,
112
+ version,
113
+ path: packagePath,
114
+ });
115
+ }
116
+ }
117
+
118
+ return dependencyMap;
119
+ }
120
+
121
+ /**
122
+ * Find duplicate versions of dependencies
123
+ */
124
+ function findDuplicateVersions(dependencyMap) {
125
+ const duplicates = [];
126
+
127
+ for (const [dep, usages] of dependencyMap.entries()) {
128
+ // Get unique versions
129
+ const versions = new Set(usages.map((u) => u.version));
130
+
131
+ if (versions.size > 1) {
132
+ // Group by version
133
+ const byVersion = new Map();
134
+ for (const usage of usages) {
135
+ if (!byVersion.has(usage.version)) {
136
+ byVersion.set(usage.version, []);
137
+ }
138
+ byVersion.get(usage.version).push(usage.package);
139
+ }
140
+
141
+ duplicates.push({
142
+ dependency: dep,
143
+ versions: Array.from(byVersion.entries()).map(([version, packages]) => ({
144
+ version,
145
+ packages,
146
+ count: packages.length,
147
+ })),
148
+ totalPackages: usages.length,
149
+ });
150
+ }
151
+ }
152
+
153
+ // Sort by number of packages affected (most impactful first)
154
+ duplicates.sort((a, b) => b.totalPackages - a.totalPackages);
155
+
156
+ return duplicates;
157
+ }
158
+
159
+ /**
160
+ * Find outdated common dependencies
161
+ */
162
+ function findOutdatedCommon(dependencyMap) {
163
+ const outdated = [];
164
+
165
+ for (const [dep, usages] of dependencyMap.entries()) {
166
+ if (usages.length < 3) {continue;} // Only check widely-used deps
167
+
168
+ const versions = usages.map((u) => u.version);
169
+ const uniqueVersions = new Set(versions);
170
+
171
+ if (uniqueVersions.size === 1) {continue;} // All same version
172
+
173
+ // Try to determine which version is newest (simple semver comparison)
174
+ const sortedVersions = Array.from(uniqueVersions).sort((a, b) => {
175
+ // Remove ^ and ~ prefixes for comparison
176
+ const cleanA = a.replace(/^[\^~]/, '');
177
+ const cleanB = b.replace(/^[\^~]/, '');
178
+
179
+ return cleanB.localeCompare(cleanA, undefined, { numeric: true });
180
+ });
181
+
182
+ const newestVersion = sortedVersions[0];
183
+ const outdatedUsages = usages.filter((u) => u.version !== newestVersion);
184
+
185
+ if (outdatedUsages.length > 0) {
186
+ outdated.push({
187
+ dependency: dep,
188
+ newestVersion,
189
+ outdatedUsages: outdatedUsages.map((u) => ({
190
+ package: u.package,
191
+ version: u.version,
192
+ })),
193
+ totalAffected: outdatedUsages.length,
194
+ });
195
+ }
196
+ }
197
+
198
+ // Sort by impact
199
+ outdated.sort((a, b) => b.totalAffected - a.totalAffected);
200
+
201
+ return outdated;
202
+ }
203
+
204
+ /**
205
+ * Find similar file names across packages (potential code duplication)
206
+ */
207
+ function findSimilarFiles(packages) {
208
+ const fileMap = new Map(); // filename -> [{package, path}]
209
+
210
+ for (const packagePath of packages) {
211
+ const packageDir = path.dirname(packagePath);
212
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
213
+ const packageName = packageJson.name;
214
+
215
+ if (!packageName || !packageName.startsWith('@kb-labs/')) {continue;}
216
+
217
+ const srcDir = path.join(packageDir, 'src');
218
+ if (!fs.existsSync(srcDir)) {continue;}
219
+
220
+ function walk(dir) {
221
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
222
+
223
+ for (const entry of entries) {
224
+ const fullPath = path.join(dir, entry.name);
225
+
226
+ if (entry.isDirectory()) {
227
+ walk(fullPath);
228
+ } else if (entry.isFile() && /\.(ts|tsx|js|jsx)$/.test(entry.name)) {
229
+ // Skip test files and common names
230
+ if (
231
+ entry.name.includes('.test.') ||
232
+ entry.name.includes('.spec.') ||
233
+ entry.name === 'index.ts' ||
234
+ entry.name === 'index.tsx' ||
235
+ entry.name === 'index.js'
236
+ ) {
237
+ continue;
238
+ }
239
+
240
+ if (!fileMap.has(entry.name)) {
241
+ fileMap.set(entry.name, []);
242
+ }
243
+
244
+ fileMap.get(entry.name).push({
245
+ package: packageName,
246
+ path: path.relative(packageDir, fullPath),
247
+ });
248
+ }
249
+ }
250
+ }
251
+
252
+ walk(srcDir);
253
+ }
254
+
255
+ // Find files that appear in multiple packages
256
+ const duplicates = [];
257
+
258
+ for (const [filename, locations] of fileMap.entries()) {
259
+ if (locations.length > 1) {
260
+ duplicates.push({
261
+ filename,
262
+ locations,
263
+ count: locations.length,
264
+ });
265
+ }
266
+ }
267
+
268
+ // Sort by frequency
269
+ duplicates.sort((a, b) => b.count - a.count);
270
+
271
+ return duplicates;
272
+ }
273
+
274
+ /**
275
+ * Main function
276
+ */
277
+ function main() {
278
+ const rootDir = process.cwd();
279
+
280
+ log('\nšŸ” KB Labs Duplicate Checker\n', 'blue');
281
+
282
+ if (options.package) {
283
+ log(`Checking package: ${options.package}\n`, 'gray');
284
+ } else {
285
+ log('Checking all packages...\n', 'gray');
286
+ }
287
+
288
+ if (options.code) {
289
+ log('šŸ“ Code duplication check enabled\n', 'cyan');
290
+ }
291
+
292
+ const packages = findPackages(rootDir, options.package);
293
+
294
+ if (packages.length === 0) {
295
+ log('āš ļø No KB Labs packages found', 'yellow');
296
+ log(' Run this command from the monorepo root\n', 'gray');
297
+ process.exit(0);
298
+ }
299
+
300
+ log(`Found ${packages.length} package(s) to check\n`, 'gray');
301
+
302
+ // Collect dependencies
303
+ const dependencyMap = collectDependencies(packages);
304
+ log(`Analyzing ${dependencyMap.size} unique dependencies...\n`, 'gray');
305
+
306
+ // Find duplicate versions
307
+ const duplicates = findDuplicateVersions(dependencyMap);
308
+
309
+ // Find outdated common deps
310
+ const outdated = findOutdatedCommon(dependencyMap);
311
+
312
+ // Find similar files (if enabled)
313
+ let similarFiles = [];
314
+ if (options.code) {
315
+ log('Scanning for code duplication patterns...\n', 'gray');
316
+ similarFiles = findSimilarFiles(packages);
317
+ }
318
+
319
+ // Print results
320
+ let hasIssues = false;
321
+
322
+ // Print duplicate versions
323
+ if (duplicates.length > 0) {
324
+ hasIssues = true;
325
+ log('šŸ”“ Duplicate Dependencies (Different Versions):\n', 'red');
326
+
327
+ for (let i = 0; i < duplicates.length; i++) {
328
+ const dup = duplicates[i];
329
+ log(`${i + 1}. ${dup.dependency}`, 'yellow');
330
+ log(` Used by ${dup.totalPackages} package(s) with ${dup.versions.length} different versions:`, 'gray');
331
+
332
+ for (const ver of dup.versions) {
333
+ log(` • ${ver.version} (${ver.count} package(s))`, 'cyan');
334
+ if (options.verbose) {
335
+ for (const pkg of ver.packages.slice(0, 3)) {
336
+ log(` - ${pkg}`, 'gray');
337
+ }
338
+ if (ver.packages.length > 3) {
339
+ log(` ... and ${ver.packages.length - 3} more`, 'gray');
340
+ }
341
+ }
342
+ }
343
+
344
+ log('', 'gray');
345
+ }
346
+
347
+ log(' šŸ’” Different versions can lead to:' , 'blue');
348
+ log(' - Larger bundle sizes (multiple copies bundled)', 'gray');
349
+ log(' - Type conflicts and build errors', 'gray');
350
+ log(' - Runtime bugs from version incompatibilities', 'gray');
351
+ log(' šŸ’” Run: pnpm dedupe or align versions manually\n', 'blue');
352
+ }
353
+
354
+ // Print outdated common dependencies
355
+ if (outdated.length > 0 && options.verbose) {
356
+ hasIssues = true;
357
+ log('🟔 Outdated Common Dependencies:\n', 'yellow');
358
+
359
+ for (let i = 0; i < Math.min(outdated.length, 10); i++) {
360
+ const dep = outdated[i];
361
+ log(`${i + 1}. ${dep.dependency}`, 'yellow');
362
+ log(` Latest: ${dep.newestVersion}`, 'green');
363
+ log(` ${dep.totalAffected} package(s) using older versions:`, 'gray');
364
+
365
+ for (const usage of dep.outdatedUsages.slice(0, 3)) {
366
+ log(` • ${usage.package}: ${usage.version}`, 'gray');
367
+ }
368
+
369
+ if (dep.outdatedUsages.length > 3) {
370
+ log(` ... and ${dep.outdatedUsages.length - 3} more`, 'gray');
371
+ }
372
+
373
+ log('', 'gray');
374
+ }
375
+
376
+ log(' šŸ’” Consider upgrading to the latest version for consistency\n', 'blue');
377
+ }
378
+
379
+ // Print similar files (code duplication)
380
+ if (similarFiles.length > 0) {
381
+ hasIssues = true;
382
+ log('🟠 Potential Code Duplication (Similar File Names):\n', 'yellow');
383
+
384
+ for (let i = 0; i < Math.min(similarFiles.length, 15); i++) {
385
+ const file = similarFiles[i];
386
+ log(`${i + 1}. ${file.filename}`, 'yellow');
387
+ log(` Found in ${file.count} package(s):`, 'gray');
388
+
389
+ for (const loc of file.locations) {
390
+ log(` • ${loc.package}`, 'cyan');
391
+ log(` ${loc.path}`, 'gray');
392
+ }
393
+
394
+ log('', 'gray');
395
+ }
396
+
397
+ log(' āš ļø This may indicate duplicated code that could be shared', 'yellow');
398
+ log(' šŸ’” Consider extracting common code to a shared package\n', 'blue');
399
+ }
400
+
401
+ // Summary
402
+ log('─'.repeat(60) + '\n', 'gray');
403
+
404
+ if (!hasIssues) {
405
+ log('āœ… No duplicate dependencies found!\n', 'green');
406
+ process.exit(0);
407
+ }
408
+
409
+ log('šŸ“Š Summary:\n', 'blue');
410
+
411
+ if (duplicates.length > 0) {
412
+ log(` šŸ”“ ${duplicates.length} dependencies with version conflicts`, 'red');
413
+ }
414
+ if (outdated.length > 0 && options.verbose) {
415
+ log(` 🟔 ${outdated.length} common dependencies with outdated versions`, 'yellow');
416
+ }
417
+ if (similarFiles.length > 0) {
418
+ log(` 🟠 ${similarFiles.length} potential code duplication patterns`, 'yellow');
419
+ }
420
+
421
+ log('\nšŸ’” Tips:', 'blue');
422
+ log(' • Use --verbose to see full package lists and outdated deps', 'gray');
423
+ log(' • Use --code to check for code duplication patterns', 'gray');
424
+ log(' • Run: pnpm dedupe to resolve version conflicts', 'gray');
425
+ log(' • Consider using pnpm overrides for enforcing versions', 'gray');
426
+ log('');
427
+
428
+ process.exit(1);
429
+ }
430
+
431
+ main();