@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,453 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @kb-labs/devkit - Quick Statistics
5
+ *
6
+ * Generates comprehensive statistics about your monorepo.
7
+ * Perfect for dashboards, metrics, and quick overview.
8
+ *
9
+ * Usage:
10
+ * kb-devkit-stats # Show all statistics
11
+ * kb-devkit-stats --json # Output JSON
12
+ * kb-devkit-stats --md # Output Markdown table
13
+ * kb-devkit-stats --health # Show health score
14
+ */
15
+
16
+ import fs from 'node:fs';
17
+ import path from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+
20
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
+
22
+ // ANSI colors
23
+ const colors = {
24
+ reset: '\x1b[0m',
25
+ red: '\x1b[31m',
26
+ green: '\x1b[32m',
27
+ yellow: '\x1b[33m',
28
+ blue: '\x1b[34m',
29
+ magenta: '\x1b[35m',
30
+ cyan: '\x1b[36m',
31
+ gray: '\x1b[90m',
32
+ bold: '\x1b[1m',
33
+ };
34
+
35
+ function log(message, color = 'reset') {
36
+ if (!options.json && !options.md) {
37
+ console.log(`${colors[color]}${message}${colors.reset}`);
38
+ }
39
+ }
40
+
41
+ // Parse CLI arguments
42
+ const args = process.argv.slice(2);
43
+ const options = {
44
+ json: args.includes('--json'),
45
+ md: args.includes('--md'),
46
+ health: args.includes('--health'),
47
+ };
48
+
49
+ /**
50
+ * Find all packages
51
+ */
52
+ function findPackages(rootDir) {
53
+ const packages = [];
54
+ const entries = fs.readdirSync(rootDir, { withFileTypes: true });
55
+
56
+ for (const entry of entries) {
57
+ if (!entry.isDirectory() || !entry.name.startsWith('kb-labs-')) {continue;}
58
+
59
+ const repoPath = path.join(rootDir, entry.name);
60
+ const packagesDir = path.join(repoPath, 'packages');
61
+
62
+ if (!fs.existsSync(packagesDir)) {continue;}
63
+
64
+ const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true });
65
+
66
+ for (const pkgDir of packageDirs) {
67
+ if (!pkgDir.isDirectory()) {continue;}
68
+
69
+ const packageJsonPath = path.join(packagesDir, pkgDir.name, 'package.json');
70
+
71
+ if (fs.existsSync(packageJsonPath)) {
72
+ packages.push(packageJsonPath);
73
+ }
74
+ }
75
+ }
76
+
77
+ return packages;
78
+ }
79
+
80
+ /**
81
+ * Calculate package size
82
+ */
83
+ function calculateSize(packageDir) {
84
+ const srcDir = path.join(packageDir, 'src');
85
+ if (!fs.existsSync(srcDir)) {
86
+ return { files: 0, lines: 0, bytes: 0 };
87
+ }
88
+
89
+ let files = 0;
90
+ let lines = 0;
91
+ let bytes = 0;
92
+
93
+ function walk(dir) {
94
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
95
+
96
+ for (const entry of entries) {
97
+ const fullPath = path.join(dir, entry.name);
98
+
99
+ if (entry.isDirectory()) {
100
+ walk(fullPath);
101
+ } else if (entry.isFile() && /\.(ts|tsx|js|jsx)$/.test(entry.name)) {
102
+ files++;
103
+ const content = fs.readFileSync(fullPath, 'utf-8');
104
+ lines += content.split('\n').length;
105
+ bytes += Buffer.byteLength(content, 'utf-8');
106
+ }
107
+ }
108
+ }
109
+
110
+ walk(srcDir);
111
+ return { files, lines, bytes };
112
+ }
113
+
114
+ /**
115
+ * Collect statistics
116
+ */
117
+ function collectStats(packages) {
118
+ const stats = {
119
+ overview: {
120
+ totalPackages: 0,
121
+ totalRepositories: 0,
122
+ totalFiles: 0,
123
+ totalLines: 0,
124
+ totalBytes: 0,
125
+ },
126
+ byRepository: {},
127
+ dependencies: {
128
+ total: 0,
129
+ workspace: 0,
130
+ external: 0,
131
+ duplicates: 0,
132
+ },
133
+ packages: [],
134
+ health: {
135
+ score: 0,
136
+ issues: [],
137
+ },
138
+ };
139
+
140
+ const repos = new Set();
141
+ const allDeps = new Map();
142
+
143
+ for (const packagePath of packages) {
144
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
145
+ const packageName = packageJson.name;
146
+
147
+ if (!packageName || !packageName.startsWith('@kb-labs/')) {continue;}
148
+
149
+ stats.overview.totalPackages++;
150
+
151
+ const repoName = packageName.split('/')[1].split('-')[0];
152
+ repos.add(repoName);
153
+
154
+ const packageDir = path.dirname(packagePath);
155
+ const size = calculateSize(packageDir);
156
+
157
+ stats.overview.totalFiles += size.files;
158
+ stats.overview.totalLines += size.lines;
159
+ stats.overview.totalBytes += size.bytes;
160
+
161
+ // By repository
162
+ if (!stats.byRepository[repoName]) {
163
+ stats.byRepository[repoName] = {
164
+ packages: 0,
165
+ files: 0,
166
+ lines: 0,
167
+ bytes: 0,
168
+ };
169
+ }
170
+
171
+ stats.byRepository[repoName].packages++;
172
+ stats.byRepository[repoName].files += size.files;
173
+ stats.byRepository[repoName].lines += size.lines;
174
+ stats.byRepository[repoName].bytes += size.bytes;
175
+
176
+ // Dependencies
177
+ const deps = {
178
+ ...packageJson.dependencies,
179
+ ...packageJson.devDependencies,
180
+ };
181
+
182
+ let workspaceDeps = 0;
183
+ let externalDeps = 0;
184
+
185
+ for (const [dep, version] of Object.entries(deps)) {
186
+ if (version.startsWith('workspace:') || version.startsWith('link:')) {
187
+ workspaceDeps++;
188
+ stats.dependencies.workspace++;
189
+ } else {
190
+ externalDeps++;
191
+ stats.dependencies.external++;
192
+
193
+ // Track for duplicates
194
+ if (!allDeps.has(dep)) {
195
+ allDeps.set(dep, new Set());
196
+ }
197
+ allDeps.get(dep).add(version);
198
+ }
199
+ }
200
+
201
+ stats.dependencies.total += Object.keys(deps).length;
202
+
203
+ // Health checks
204
+ const hasReadme = fs.existsSync(path.join(packageDir, 'README.md'));
205
+ const hasSrc = fs.existsSync(path.join(packageDir, 'src'));
206
+ const hasTsconfig = fs.existsSync(path.join(packageDir, 'tsconfig.json'));
207
+
208
+ stats.packages.push({
209
+ name: packageName,
210
+ repository: repoName,
211
+ files: size.files,
212
+ lines: size.lines,
213
+ bytes: size.bytes,
214
+ dependencies: {
215
+ total: Object.keys(deps).length,
216
+ workspace: workspaceDeps,
217
+ external: externalDeps,
218
+ },
219
+ health: {
220
+ hasReadme,
221
+ hasSrc,
222
+ hasTsconfig,
223
+ },
224
+ });
225
+ }
226
+
227
+ stats.overview.totalRepositories = repos.size;
228
+
229
+ // Count duplicates
230
+ for (const [dep, versions] of allDeps.entries()) {
231
+ if (versions.size > 1) {
232
+ stats.dependencies.duplicates++;
233
+ }
234
+ }
235
+
236
+ // Calculate health score
237
+ stats.health = calculateHealthScore(stats);
238
+
239
+ return stats;
240
+ }
241
+
242
+ /**
243
+ * Calculate overall health score
244
+ */
245
+ function calculateHealthScore(stats) {
246
+ let score = 100;
247
+ const issues = [];
248
+
249
+ // Deduct for duplicates
250
+ if (stats.dependencies.duplicates > 0) {
251
+ const deduction = Math.min(20, stats.dependencies.duplicates * 2);
252
+ score -= deduction;
253
+ issues.push({
254
+ type: 'duplicates',
255
+ severity: 'high',
256
+ message: `${stats.dependencies.duplicates} duplicate dependencies`,
257
+ impact: -deduction,
258
+ });
259
+ }
260
+
261
+ // Deduct for missing READMEs
262
+ const missingReadmes = stats.packages.filter((p) => !p.health.hasReadme).length;
263
+ if (missingReadmes > 0) {
264
+ const deduction = Math.min(15, missingReadmes);
265
+ score -= deduction;
266
+ issues.push({
267
+ type: 'documentation',
268
+ severity: 'medium',
269
+ message: `${missingReadmes} packages missing README`,
270
+ impact: -deduction,
271
+ });
272
+ }
273
+
274
+ // Deduct for missing src
275
+ const missingSrc = stats.packages.filter((p) => !p.health.hasSrc).length;
276
+ if (missingSrc > 0) {
277
+ const deduction = Math.min(10, missingSrc * 2);
278
+ score -= deduction;
279
+ issues.push({
280
+ type: 'structure',
281
+ severity: 'high',
282
+ message: `${missingSrc} packages missing src directory`,
283
+ impact: -deduction,
284
+ });
285
+ }
286
+
287
+ // Bonus for good practices
288
+ if (stats.overview.totalPackages > 50 && score > 80) {
289
+ score = Math.min(100, score + 5);
290
+ issues.push({
291
+ type: 'scale',
292
+ severity: 'info',
293
+ message: 'Well-maintained large monorepo',
294
+ impact: +5,
295
+ });
296
+ }
297
+
298
+ return {
299
+ score: Math.max(0, Math.round(score)),
300
+ grade: score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : score >= 60 ? 'D' : 'F',
301
+ issues,
302
+ };
303
+ }
304
+
305
+ /**
306
+ * Format bytes
307
+ */
308
+ function formatBytes(bytes) {
309
+ if (bytes === 0) {return '0 B';}
310
+ const k = 1024;
311
+ const sizes = ['B', 'KB', 'MB', 'GB'];
312
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
313
+ return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
314
+ }
315
+
316
+ /**
317
+ * Print statistics
318
+ */
319
+ function printStats(stats) {
320
+ log('\n📊 KB Labs Monorepo Statistics\n', 'bold');
321
+
322
+ // Overview
323
+ log('📦 Overview:', 'blue');
324
+ log(` Packages: ${stats.overview.totalPackages}`, 'cyan');
325
+ log(` Repositories: ${stats.overview.totalRepositories}`, 'cyan');
326
+ log(` Files: ${stats.overview.totalFiles.toLocaleString()}`, 'cyan');
327
+ log(` Lines of Code: ${stats.overview.totalLines.toLocaleString()}`, 'cyan');
328
+ log(` Total Size: ${formatBytes(stats.overview.totalBytes)}`, 'cyan');
329
+ log('', 'reset');
330
+
331
+ // Dependencies
332
+ log('🔗 Dependencies:', 'blue');
333
+ log(` Total: ${stats.dependencies.total.toLocaleString()}`, 'cyan');
334
+ log(` Workspace: ${stats.dependencies.workspace.toLocaleString()}`, 'green');
335
+ log(` External: ${stats.dependencies.external.toLocaleString()}`, 'yellow');
336
+ if (stats.dependencies.duplicates > 0) {
337
+ log(` Duplicates: ${stats.dependencies.duplicates} ⚠️`, 'red');
338
+ } else {
339
+ log(` Duplicates: 0 ✅`, 'green');
340
+ }
341
+ log('', 'reset');
342
+
343
+ // By Repository
344
+ log('📁 By Repository:', 'blue');
345
+ const repos = Object.entries(stats.byRepository).sort((a, b) => b[1].packages - a[1].packages);
346
+
347
+ for (const [repo, data] of repos.slice(0, 10)) {
348
+ log(` ${repo.padEnd(15)} ${data.packages.toString().padStart(3)} pkg ${data.lines.toLocaleString().padStart(8)} lines`, 'gray');
349
+ }
350
+ log('', 'reset');
351
+
352
+ // Health Score
353
+ if (options.health) {
354
+ log('💚 Health Score:', 'blue');
355
+ const scoreColor =
356
+ stats.health.grade === 'A' ? 'green' :
357
+ stats.health.grade === 'B' ? 'cyan' :
358
+ stats.health.grade === 'C' ? 'yellow' : 'red';
359
+
360
+ log(` Score: ${stats.health.score}/100 (Grade ${stats.health.grade})`, scoreColor);
361
+
362
+ if (stats.health.issues.length > 0) {
363
+ log('\n Issues:', 'yellow');
364
+ for (const issue of stats.health.issues) {
365
+ const icon =
366
+ issue.severity === 'high' ? '🔴' :
367
+ issue.severity === 'medium' ? '🟡' : 'ℹ️';
368
+ const impact = issue.impact > 0 ? `+${issue.impact}` : issue.impact;
369
+ log(` ${icon} ${issue.message} (${impact})`, 'gray');
370
+ }
371
+ }
372
+ log('', 'reset');
373
+ }
374
+
375
+ // Top packages
376
+ const largest = stats.packages.sort((a, b) => b.lines - a.lines).slice(0, 5);
377
+ log('📈 Largest Packages:', 'blue');
378
+ for (let i = 0; i < largest.length; i++) {
379
+ const pkg = largest[i];
380
+ log(` ${i + 1}. ${pkg.name}`, 'yellow');
381
+ log(` ${pkg.lines.toLocaleString()} lines, ${pkg.files} files`, 'gray');
382
+ }
383
+ log('', 'reset');
384
+ }
385
+
386
+ /**
387
+ * Print Markdown
388
+ */
389
+ function printMarkdown(stats) {
390
+ console.log('# KB Labs Monorepo Statistics\n');
391
+
392
+ console.log('## Overview\n');
393
+ console.log('| Metric | Value |');
394
+ console.log('|--------|-------|');
395
+ console.log(`| Packages | ${stats.overview.totalPackages} |`);
396
+ console.log(`| Repositories | ${stats.overview.totalRepositories} |`);
397
+ console.log(`| Files | ${stats.overview.totalFiles.toLocaleString()} |`);
398
+ console.log(`| Lines of Code | ${stats.overview.totalLines.toLocaleString()} |`);
399
+ console.log(`| Total Size | ${formatBytes(stats.overview.totalBytes)} |`);
400
+ console.log('');
401
+
402
+ console.log('## By Repository\n');
403
+ console.log('| Repository | Packages | Lines | Size |');
404
+ console.log('|------------|----------|-------|------|');
405
+
406
+ const repos = Object.entries(stats.byRepository).sort((a, b) => b[1].packages - a[1].packages);
407
+ for (const [repo, data] of repos) {
408
+ console.log(`| ${repo} | ${data.packages} | ${data.lines.toLocaleString()} | ${formatBytes(data.bytes)} |`);
409
+ }
410
+ console.log('');
411
+
412
+ if (options.health) {
413
+ console.log('## Health Score\n');
414
+ console.log(`**Score:** ${stats.health.score}/100 (Grade ${stats.health.grade})\n`);
415
+
416
+ if (stats.health.issues.length > 0) {
417
+ console.log('### Issues\n');
418
+ for (const issue of stats.health.issues) {
419
+ const emoji = issue.severity === 'high' ? '🔴' : issue.severity === 'medium' ? '🟡' : 'ℹ️';
420
+ console.log(`- ${emoji} ${issue.message}`);
421
+ }
422
+ console.log('');
423
+ }
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Main function
429
+ */
430
+ function main() {
431
+ const rootDir = process.cwd();
432
+
433
+ const packages = findPackages(rootDir);
434
+
435
+ if (packages.length === 0) {
436
+ log('⚠️ No KB Labs packages found', 'yellow');
437
+ process.exit(0);
438
+ }
439
+
440
+ const stats = collectStats(packages);
441
+
442
+ if (options.json) {
443
+ console.log(JSON.stringify(stats, null, 2));
444
+ } else if (options.md) {
445
+ printMarkdown(stats);
446
+ } else {
447
+ printStats(stats);
448
+ }
449
+
450
+ process.exit(0);
451
+ }
452
+
453
+ main();
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import { run } from '../sync/index.mjs';
3
+
4
+ (async () => {
5
+ try {
6
+ const code = await run({ args: process.argv.slice(2) });
7
+ process.exit(typeof code === 'number' ? code : 0);
8
+ } catch (e) {
9
+ console.error('[devkit-sync]', e?.message || e);
10
+ process.exit(1);
11
+ }
12
+ })();
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { dirname, resolve, join } from 'node:path';
4
+ import { promises as fs } from 'node:fs';
5
+ import process from 'node:process';
6
+ import { glob } from 'glob';
7
+ import { parse as parseYaml } from 'yaml';
8
+
9
+ async function exists(path) {
10
+ try {
11
+ await fs.access(path);
12
+ return true;
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+
18
+ async function readJson(path) {
19
+ return JSON.parse(await fs.readFile(path, 'utf8'));
20
+ }
21
+
22
+ async function findWorkspaceConfigs(startDir) {
23
+ const roots = [];
24
+ let current = resolve(startDir);
25
+ let prev = null;
26
+ while (current !== prev) {
27
+ const wsFile = join(current, 'pnpm-workspace.yaml');
28
+ if (await exists(wsFile)) {
29
+ try {
30
+ const yaml = await fs.readFile(wsFile, 'utf8');
31
+ const data = parseYaml(yaml);
32
+ const patterns = Array.isArray(data?.packages) ? data.packages.filter(Boolean) : [];
33
+ roots.push({ dir: current, patterns });
34
+ } catch (error) {
35
+ console.warn('[devkit-tsup-external] failed to read workspace config', wsFile, error);
36
+ }
37
+ }
38
+ prev = current;
39
+ current = dirname(current);
40
+ }
41
+ return roots;
42
+ }
43
+
44
+ async function collectWorkspacePackages(workspaceRoots) {
45
+ const packages = new Set();
46
+
47
+ for (const { dir, patterns } of workspaceRoots) {
48
+ for (const pattern of patterns) {
49
+ if (!pattern || typeof pattern !== 'string') {continue;}
50
+ const searchPattern = pattern.endsWith('/') ? `${pattern}package.json` : join(pattern, 'package.json');
51
+ const matches = await glob(searchPattern, {
52
+ cwd: dir,
53
+ dot: false,
54
+ nodir: true,
55
+ ignore: ['**/node_modules/**', '**/dist/**'],
56
+ });
57
+ for (const match of matches) {
58
+ try {
59
+ const pkgPath = resolve(dir, match);
60
+ const pkg = await readJson(pkgPath);
61
+ if (!pkg?.name || typeof pkg.name !== 'string') {continue;}
62
+ if (!pkg.name.startsWith('@kb-labs/')) {continue;}
63
+ packages.add(pkg.name);
64
+ } catch (error) {
65
+ console.warn('[devkit-tsup-external] failed to read package', match, error);
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ return Array.from(packages).sort();
72
+ }
73
+
74
+ /**
75
+ * Resolves external dependencies for tsup bundling.
76
+ * Returns an array of package names that should be marked as external:
77
+ * - All @kb-labs/* workspace packages
78
+ * - All dependencies and peerDependencies from the current package.json
79
+ *
80
+ * @param {string} [cwd] - Current working directory (defaults to process.cwd())
81
+ * @returns {Promise<string[]>} Array of external package names
82
+ */
83
+ export async function resolveTsupExternal(cwd = process.cwd()) {
84
+ const workspaceRoots = await findWorkspaceConfigs(cwd);
85
+ if (!workspaceRoots.length) {
86
+ console.warn('[devkit-tsup-external] no pnpm-workspace.yaml found up the tree');
87
+ return [];
88
+ }
89
+
90
+ const workspacePackages = await collectWorkspacePackages(workspaceRoots);
91
+
92
+ // Also include dependencies from current package.json
93
+ const pkgPath = join(cwd, 'package.json');
94
+ let localDeps = [];
95
+ if (await exists(pkgPath)) {
96
+ try {
97
+ const pkg = await readJson(pkgPath);
98
+ const deps = Object.keys(pkg.dependencies ?? {});
99
+ const peerDeps = Object.keys(pkg.peerDependencies ?? {});
100
+ localDeps = Array.from(new Set([...deps, ...peerDeps]));
101
+ } catch (error) {
102
+ console.warn('[devkit-tsup-external] failed to read local package.json', error);
103
+ }
104
+ }
105
+
106
+ // Combine workspace packages and local dependencies, remove duplicates
107
+ return Array.from(new Set([...workspacePackages, ...localDeps])).sort();
108
+ }
109
+
110
+ // CLI mode: generate tsup.external.json file
111
+ async function generateExternalFile(rootDir) {
112
+ const externals = await resolveTsupExternal(rootDir);
113
+
114
+ const output = {
115
+ $schema: 'https://json.schemastore.org/tsconfig',
116
+ externals: externals,
117
+ };
118
+
119
+ const outPath = join(rootDir, 'tsup.external.json');
120
+ await fs.writeFile(outPath, `${JSON.stringify(output, null, 2)}\n`);
121
+ console.log(`[devkit-tsup-external] wrote ${externals.length} external packages to ${outPath}`);
122
+ }
123
+
124
+ async function main() {
125
+ const args = process.argv.slice(2);
126
+
127
+ // Default behavior: generate file
128
+ if (args.length === 0 || args.includes('--generate') || args.includes('-g')) {
129
+ const rootDir = process.cwd();
130
+ await generateExternalFile(rootDir);
131
+ } else if (args.includes('--print') || args.includes('-p')) {
132
+ // Print externals (for testing)
133
+ const externals = await resolveTsupExternal();
134
+ console.log(JSON.stringify(externals, null, 2));
135
+ } else {
136
+ console.error('Usage: kb-devkit-tsup-external [--generate|-g] [--print|-p]');
137
+ process.exit(1);
138
+ }
139
+ }
140
+
141
+ // Only run main if this is the entry point
142
+ if (import.meta.url === `file://${process.argv[1]}`) {
143
+ main().catch((error) => {
144
+ console.error('[devkit-tsup-external] failed', error);
145
+ process.exit(1);
146
+ });
147
+ }
148
+