agemon 6.0.0 → 6.3.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 (171) hide show
  1. package/README.md +313 -64
  2. package/assets/skills/design/agemon-design/NOTICE.md +34 -0
  3. package/assets/skills/design/agemon-design/SKILL.md +95 -358
  4. package/assets/skills/design/agemon-design/references/actions.md +215 -0
  5. package/assets/skills/design/agemon-design/references/brief.md +13 -9
  6. package/assets/skills/design/agemon-design/references/core-rules.md +166 -0
  7. package/assets/skills/design/agemon-design/references/craft-intent.md +16 -14
  8. package/assets/skills/design/agemon-design/references/dashboard.md +1 -1
  9. package/assets/skills/design/agemon-design/references/dataviz.md +8 -8
  10. package/assets/skills/design/agemon-design/references/finish-bar.md +1 -1
  11. package/assets/skills/design/agemon-design/references/heuristics.md +56 -140
  12. package/assets/skills/design/agemon-design/references/inspiration.md +6 -6
  13. package/assets/skills/design/agemon-design/references/loops.md +9 -9
  14. package/assets/skills/design/agemon-design/references/motion.md +1 -1
  15. package/assets/skills/design/agemon-design/references/personas.md +4 -4
  16. package/assets/skills/design/{agemon-design-dashboard/SKILL.md → agemon-design/references/preset-dashboard.md} +8 -12
  17. package/assets/skills/design/{agemon-design-editorial/SKILL.md → agemon-design/references/preset-editorial.md} +5 -9
  18. package/assets/skills/design/{agemon-design-minimal/SKILL.md → agemon-design/references/preset-minimal.md} +8 -12
  19. package/assets/skills/design/agemon-design/references/principles-catalog.md +3 -3
  20. package/assets/skills/design/agemon-design/references/recipe-auth.md +3 -3
  21. package/assets/skills/design/agemon-design/references/recipe-dashboard.md +2 -2
  22. package/assets/skills/design/agemon-design/references/recipe-landing.md +9 -11
  23. package/assets/skills/design/agemon-design/references/review.md +7 -7
  24. package/assets/skills/design/agemon-design/references/routing.md +143 -0
  25. package/assets/skills/design/agemon-design/references/spec.md +8 -8
  26. package/assets/skills/design/agemon-design/references/stack.md +1 -1
  27. package/assets/skills/design/agemon-design/references/stage-assets.md +106 -0
  28. package/assets/skills/design/agemon-design/references/stage-build.md +46 -0
  29. package/assets/skills/design/agemon-design/references/stage-critique.md +34 -0
  30. package/assets/skills/design/agemon-design/references/stage-directions.md +44 -0
  31. package/assets/skills/design/agemon-design/references/stage-intake.md +34 -0
  32. package/assets/skills/design/agemon-design/references/stage-verify.md +44 -0
  33. package/assets/skills/design/agemon-design/references/state-design.md +1 -1
  34. package/assets/skills/design/agemon-design/references/tells-2026-11.md +32 -0
  35. package/assets/skills/design/agemon-design/references/themes.md +24 -20
  36. package/assets/skills/design/agemon-design/references/third-party-licenses.md +323 -0
  37. package/assets/skills/design/agemon-design/references/typography.md +11 -9
  38. package/assets/skills/design/agemon-design/scripts/audit-page.js +532 -0
  39. package/assets/skills/design/agemon-design/scripts/check-source.mjs +358 -0
  40. package/assets/skills/design/agemon-design/scripts/deal.mjs +231 -0
  41. package/assets/skills/design/agemon-design/scripts/inventory.mjs +676 -0
  42. package/assets/skills/{design/agemon-design-dashboard → essentials/web-design-guidelines}/LICENSE +1 -1
  43. package/assets/skills/essentials/web-design-guidelines/NOTICE.md +14 -0
  44. package/assets/skills/essentials/web-design-guidelines/SKILL.md +18 -0
  45. package/assets/skills/essentials/web-design-guidelines/references/guidelines.md +184 -0
  46. package/assets/skills/{design/agemon-design-minimal → essentials/writing-guidelines}/LICENSE +1 -1
  47. package/assets/skills/essentials/writing-guidelines/NOTICE.md +14 -0
  48. package/assets/skills/essentials/writing-guidelines/SKILL.md +18 -0
  49. package/assets/skills/essentials/writing-guidelines/references/guidelines.md +250 -0
  50. package/assets/skills/founder/agemon-founder-board/LICENSE +21 -0
  51. package/assets/skills/founder/agemon-founder-board/NOTICE.md +11 -0
  52. package/assets/skills/founder/agemon-founder-board/SKILL.md +111 -0
  53. package/assets/skills/founder/agemon-founder-board/lenses.md +84 -0
  54. package/assets/skills/founder/agemon-founder-brand/LICENSE +21 -0
  55. package/assets/skills/founder/agemon-founder-brand/NOTICE.md +12 -0
  56. package/assets/skills/founder/agemon-founder-brand/SKILL.md +77 -0
  57. package/assets/skills/founder/agemon-founder-cfo/LICENSE +21 -0
  58. package/assets/skills/founder/agemon-founder-cfo/NOTICE.md +12 -0
  59. package/assets/skills/founder/agemon-founder-cfo/SKILL.md +109 -0
  60. package/assets/skills/founder/agemon-founder-cfo/example.json +27 -0
  61. package/assets/skills/founder/agemon-founder-cfo/unit_economics.py +270 -0
  62. package/assets/skills/founder/agemon-founder-competitors/LICENSE +21 -0
  63. package/assets/skills/founder/agemon-founder-competitors/NOTICE.md +12 -0
  64. package/assets/skills/founder/agemon-founder-competitors/SKILL.md +84 -0
  65. package/assets/skills/founder/agemon-founder-consumer/LICENSE +21 -0
  66. package/assets/skills/founder/agemon-founder-consumer/NOTICE.md +11 -0
  67. package/assets/skills/founder/agemon-founder-consumer/SKILL.md +126 -0
  68. package/assets/skills/founder/agemon-founder-consumer/customer.example.json +34 -0
  69. package/assets/skills/founder/agemon-founder-consumer/panel.py +424 -0
  70. package/assets/skills/founder/agemon-founder-consumer/pitch.example.md +3 -0
  71. package/assets/skills/founder/agemon-founder-launch/LICENSE +21 -0
  72. package/assets/skills/founder/agemon-founder-launch/NOTICE.md +11 -0
  73. package/assets/skills/founder/agemon-founder-launch/SKILL.md +65 -0
  74. package/assets/skills/founder/agemon-founder-marketing/LICENSE +21 -0
  75. package/assets/skills/founder/agemon-founder-marketing/NOTICE.md +11 -0
  76. package/assets/skills/founder/agemon-founder-marketing/SKILL.md +80 -0
  77. package/assets/skills/founder/agemon-founder-offer/LICENSE +21 -0
  78. package/assets/skills/founder/agemon-founder-offer/NOTICE.md +11 -0
  79. package/assets/skills/founder/agemon-founder-offer/SKILL.md +75 -0
  80. package/assets/skills/founder/agemon-founder-ops/LICENSE +21 -0
  81. package/assets/skills/founder/agemon-founder-ops/NOTICE.md +12 -0
  82. package/assets/skills/founder/agemon-founder-ops/SKILL.md +77 -0
  83. package/assets/skills/founder/agemon-founder-plan/LICENSE +21 -0
  84. package/assets/skills/founder/agemon-founder-plan/NOTICE.md +11 -0
  85. package/assets/skills/founder/agemon-founder-plan/SKILL.md +88 -0
  86. package/assets/skills/founder/agemon-founder-plan/compile.py +178 -0
  87. package/assets/skills/founder/agemon-founder-pricing/LICENSE +21 -0
  88. package/assets/skills/founder/agemon-founder-pricing/NOTICE.md +12 -0
  89. package/assets/skills/founder/agemon-founder-pricing/SKILL.md +92 -0
  90. package/assets/skills/founder/agemon-founder-pricing/van_westendorp.py +172 -0
  91. package/assets/skills/{design/agemon-design-editorial → output-style/agemon-adhd}/LICENSE +1 -1
  92. package/assets/skills/output-style/agemon-adhd/NOTICE.md +13 -0
  93. package/assets/skills/output-style/agemon-adhd/SKILL.md +138 -0
  94. package/assets/skills/security/agemon-security-audit/AI-AND-LLM.md +83 -0
  95. package/assets/skills/security/agemon-security-audit/ATTACK-CLASSES.md +130 -0
  96. package/assets/skills/security/agemon-security-audit/CLIENT-SIDE.md +83 -0
  97. package/assets/skills/security/agemon-security-audit/CLOUD-AND-DEPLOYMENT.md +86 -0
  98. package/assets/skills/security/agemon-security-audit/DATA-ISOLATION-AND-LIFECYCLE.md +84 -0
  99. package/assets/skills/security/agemon-security-audit/DESKTOP-MOBILE-AND-LOCAL-IPC.md +89 -0
  100. package/assets/skills/security/agemon-security-audit/HUNTING.md +251 -0
  101. package/assets/skills/security/agemon-security-audit/LICENSE +21 -0
  102. package/assets/skills/security/agemon-security-audit/MEMORY-SAFETY-AND-BINARY.md +101 -0
  103. package/assets/skills/security/agemon-security-audit/NOTICE.md +12 -0
  104. package/assets/skills/security/agemon-security-audit/PROTOCOLS-RPC-AND-MESSAGING.md +81 -0
  105. package/assets/skills/security/agemon-security-audit/RECONNAISSANCE.md +156 -0
  106. package/assets/skills/security/agemon-security-audit/RESOURCE-EXHAUSTION-AND-AVAILABILITY.md +78 -0
  107. package/assets/skills/security/agemon-security-audit/SKILL.md +192 -0
  108. package/assets/skills/security/agemon-security-audit/SUPPLY-CHAIN-AND-RELEASE.md +73 -0
  109. package/assets/skills/security/agemon-security-audit/VALIDATION-AND-REPORTING.md +186 -0
  110. package/assets/skills/security/agemon-security-audit/WEB-PROTOCOL-AND-AUTH.md +105 -0
  111. package/assets/skills/security/agemon-security-audit/report-schema.json +461 -0
  112. package/assets/skills/security/agemon-security-audit/validate-coverage-ledger.cjs +872 -0
  113. package/assets/skills/security/agemon-security-audit/validate-findings.cjs +773 -0
  114. package/assets/skills/workflow/agemon-brainstorming/LICENSE +21 -0
  115. package/assets/skills/workflow/agemon-brainstorming/NOTICE.md +14 -0
  116. package/assets/skills/workflow/agemon-brainstorming/SKILL.md +287 -0
  117. package/assets/skills/workflow/agemon-brainstorming/builder-check-prompt.md +23 -0
  118. package/assets/skills/workflow/agemon-dispatching-parallel-agents/LICENSE +21 -0
  119. package/assets/skills/workflow/agemon-dispatching-parallel-agents/NOTICE.md +13 -0
  120. package/assets/skills/workflow/agemon-dispatching-parallel-agents/SKILL.md +167 -0
  121. package/assets/skills/workflow/agemon-executing-plans/LICENSE +21 -0
  122. package/assets/skills/workflow/agemon-executing-plans/NOTICE.md +15 -0
  123. package/assets/skills/workflow/agemon-executing-plans/SKILL.md +322 -0
  124. package/assets/skills/workflow/agemon-executing-plans/scripts/task-done +55 -0
  125. package/assets/skills/workflow/agemon-executing-plans/scripts/task-start +32 -0
  126. package/assets/skills/workflow/agemon-finishing-a-development-branch/LICENSE +21 -0
  127. package/assets/skills/workflow/agemon-finishing-a-development-branch/NOTICE.md +13 -0
  128. package/assets/skills/workflow/agemon-finishing-a-development-branch/SKILL.md +225 -0
  129. package/assets/skills/workflow/agemon-subagent-driven-development/LICENSE +21 -0
  130. package/assets/skills/workflow/agemon-subagent-driven-development/NOTICE.md +15 -0
  131. package/assets/skills/workflow/agemon-subagent-driven-development/SKILL.md +339 -0
  132. package/assets/skills/workflow/agemon-subagent-driven-development/code-reviewer.md +198 -0
  133. package/assets/skills/workflow/agemon-subagent-driven-development/implementer-prompt.md +154 -0
  134. package/assets/skills/workflow/agemon-subagent-driven-development/re-review-prompt.md +116 -0
  135. package/assets/skills/workflow/agemon-subagent-driven-development/references/batching.md +11 -0
  136. package/assets/skills/workflow/agemon-subagent-driven-development/references/cannot-verify.md +10 -0
  137. package/assets/skills/workflow/agemon-subagent-driven-development/references/final-review.md +49 -0
  138. package/assets/skills/workflow/agemon-subagent-driven-development/references/fix-loop.md +77 -0
  139. package/assets/skills/workflow/agemon-subagent-driven-development/references/handling-reports.md +15 -0
  140. package/assets/skills/workflow/agemon-subagent-driven-development/references/preflight.md +26 -0
  141. package/assets/skills/workflow/agemon-subagent-driven-development/references/resuming.md +12 -0
  142. package/assets/skills/workflow/agemon-subagent-driven-development/references/role-scout.md +18 -0
  143. package/assets/skills/workflow/agemon-subagent-driven-development/references/role-worker.md +19 -0
  144. package/assets/skills/workflow/agemon-subagent-driven-development/scripts/review-package +64 -0
  145. package/assets/skills/workflow/agemon-subagent-driven-development/scripts/sdd-repo +54 -0
  146. package/assets/skills/workflow/agemon-subagent-driven-development/scripts/sdd-workspace +110 -0
  147. package/assets/skills/workflow/agemon-subagent-driven-development/scripts/task-brief +108 -0
  148. package/assets/skills/workflow/agemon-subagent-driven-development/task-reviewer-prompt.md +209 -0
  149. package/assets/skills/workflow/agemon-test-driven-development/LICENSE +21 -0
  150. package/assets/skills/workflow/agemon-test-driven-development/NOTICE.md +14 -0
  151. package/assets/skills/workflow/agemon-test-driven-development/SKILL.md +330 -0
  152. package/assets/skills/workflow/agemon-test-driven-development/writing-good-tests.md +198 -0
  153. package/assets/skills/workflow/agemon-using-git-worktrees/LICENSE +21 -0
  154. package/assets/skills/workflow/agemon-using-git-worktrees/NOTICE.md +13 -0
  155. package/assets/skills/workflow/agemon-using-git-worktrees/SKILL.md +167 -0
  156. package/assets/skills/workflow/agemon-writing-plans/LICENSE +21 -0
  157. package/assets/skills/workflow/agemon-writing-plans/NOTICE.md +13 -0
  158. package/assets/skills/workflow/agemon-writing-plans/SKILL.md +204 -0
  159. package/dist/{chunk-5LATHJYU.js → chunk-ZZBF3GOV.js} +369 -944
  160. package/dist/chunk-ZZBF3GOV.js.map +1 -0
  161. package/dist/{fake-subprocess-2BETWZVZ.js → fake-subprocess-IV4ML77S.js} +3 -33
  162. package/dist/fake-subprocess-IV4ML77S.js.map +1 -0
  163. package/dist/index.js +3477 -731
  164. package/dist/index.js.map +1 -1
  165. package/package.json +8 -1
  166. package/assets/skills/design/agemon-design/references/agents.md +0 -63
  167. package/assets/skills/design/agemon-design-dashboard/NOTICE.md +0 -9
  168. package/assets/skills/design/agemon-design-editorial/NOTICE.md +0 -9
  169. package/assets/skills/design/agemon-design-minimal/NOTICE.md +0 -9
  170. package/dist/chunk-5LATHJYU.js.map +0 -1
  171. package/dist/fake-subprocess-2BETWZVZ.js.map +0 -1
@@ -0,0 +1,676 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Read-only design asset inventory. Usage: node inventory.mjs [root]
4
+ // Prints JSON to stdout and writes nothing. Only typed values leave this file
5
+ // (colour, dimension, font name, path), because the repo it reads is untrusted.
6
+
7
+ import { readdirSync, readFileSync, statSync } from "node:fs";
8
+ import { dirname, join, relative, resolve, sep } from "node:path";
9
+
10
+ const MAX_FILES = 5000;
11
+ const MAX_FILE_BYTES = 1024 * 1024;
12
+ const MAX_VALUE_LENGTH = 120;
13
+ const MAX_FONT_WORDS = 5;
14
+ const MAX_FRONT_MATTER_LINE_LENGTH = 300;
15
+ const MAX_TOTAL_BYTES = 64 * 1024 * 1024;
16
+ const MAX_TOKENS = 2000;
17
+ const MAX_LISTED_ITEMS = 100;
18
+ const MAX_CSS_DEPTH = 64;
19
+ const MAX_PATH_SPACES = 3;
20
+ const MAX_PATH_SEGMENT_LENGTH = 64;
21
+ const TOP_FREQUENCIES = 20;
22
+ const EXIT_BAD_ROOT = 2;
23
+
24
+ const SKIPPED_DIRECTORIES = new Set([
25
+ "node_modules", "dist", "build", "out", "coverage", "vendor", "storybook-static",
26
+ ]);
27
+ const READABLE_HIDDEN_DIRECTORIES = new Set([".ui-craft"]);
28
+
29
+ const SAFE_PATH = /^[\p{L}\p{N}_ ./@()+[\]-]{1,200}$/u;
30
+ const SAFE_NAME = /^[A-Za-z-][\w-]{0,63}$/;
31
+ const STYLE_FILE = /\.(?:css|scss|pcss)$/;
32
+ const MINIFIED_FILE = /\.min\.[a-z]+$/;
33
+ const BUILT_STYLE_FILE = /(?:^|\/)(?:output|compiled|bundle|generated)[^/]*\.css$/;
34
+ const TAILWIND_BUILD_HEADER = /^\s*\/\*!?\s*!?\s*tailwindcss v\d/;
35
+ const TAILWIND_COMPILED_THEME = /@layer theme\s*\{\s*:root,\s*:host/;
36
+ const CODE_FILE = /\.(?:tsx?|jsx?|mjs|cjs|html|vue|svelte|astro|mdx)$/;
37
+ const TAILWIND_CONFIG_FILE = /(?:^|\/)tailwind\.config\.(?:js|cjs|mjs|ts|cts|mts)$/;
38
+ const DESIGN_CONTRACT_FILE = /^(?:DESIGN|design)\.md$/;
39
+ const CONTRACT_FILE =
40
+ /^(?:(?:DESIGN|design|PRODUCT|BRAND)\.md|\.ui-craft\/(?:brief|tokens)\.md|docs\/brand[^/]*|(?:.*\/)?design-tokens\.json|(?:.*\/)?[^/]+\.tokens(?:\.json)?)$/;
41
+ const LOGO_FILE =
42
+ /(?:^|[/_. -])(?:logo|logomark|wordmark|brandmark)[^/]*\.(?:svg|png|webp|jpe?g|ico|avif)$/i;
43
+
44
+ const HEX_COLOR = /^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/;
45
+ const COLOR_FUNCTION =
46
+ /^(?:rgba?|hsla?|oklch|oklab|lab|lch|hwb|color)\((?:[\d.,%\s/+-]|none|deg|rad|turn|srgb|display-p3)+\)$/;
47
+ const HSL_CHANNELS = /^\d+(?:\.\d+)?\s+\d+(?:\.\d+)?%\s+\d+(?:\.\d+)?%$/;
48
+ const DIMENSION =
49
+ /^(?:0|-?(?:\d+\.?\d*|\.\d+)(?:px|rem|em|ch|ex|vh|vw|svh|dvh|lvh|vmin|vmax|%))$/;
50
+ const FONT_NAME = /^[A-Za-z][A-Za-z0-9 -]{0,47}$/;
51
+ const FONT_TOKEN_NAME = /font(?![-_]?(?:size|weight|style|stretch|feature|variation))/i;
52
+ const GENERIC_FONT =
53
+ /^(?:serif|sans-serif|monospace|cursive|fantasy|math|emoji|fangsong|inherit|initial|unset|revert|none|system-ui|ui-[a-z-]+|-apple-system|BlinkMacSystemFont)$/i;
54
+
55
+ // The lookbehinds keep fragment links out: href="#add" is not a colour.
56
+ const HEX_IN_TEXT = /(?<![\w&/])(?<!href=["'])#[0-9a-fA-F]{3,8}\b/g;
57
+ const COLOR_FUNCTION_IN_TEXT = /\b(?:rgba?|hsla?|oklch|oklab|lab|lch|hwb|color)\([^()]{1,80}\)/gi;
58
+ const ROUNDED_CLASS = /\brounded-(none|xs|sm|md|lg|xl|2xl|3xl|full)\b/g;
59
+ const ROUNDED_ARBITRARY_CLASS = /\brounded-\[([^\]\s]{1,40})\]/g;
60
+ const JS_BORDER_RADIUS = /\bborderRadius:\s*["'`]([^"'`\n]{1,40})["'`]/g;
61
+ const NEXT_FONT_IMPORT = /import\s*\{([^}]{1,300})\}\s*from\s*["']next\/font\/google["']/g;
62
+ const FONTSOURCE_IMPORT = /@fontsource(?:-variable)?\/([a-z0-9-]{1,60})/g;
63
+ const GOOGLE_FONTS_URL = /fonts\.googleapis\.com\/css2?\?[^"'\s)]{1,500}/g;
64
+ const GOOGLE_FONT_FAMILY = /family=([^&:;]+)/g;
65
+ const CONFIG_STRING_ENTRY = /(?<![\w-])(["']?)([A-Za-z][\w-]{0,63})\1\s*:\s*["']([^"'\n]{1,120})["']/g;
66
+ const CONFIG_FONT_BLOCK = /fontFamily\s*:\s*\{([^}]{0,1000})\}/g;
67
+ const CONFIG_FONT_ENTRY = /(?<![\w-])(["']?)([A-Za-z][\w-]{0,63})\1\s*:\s*\[?\s*["']([^"'\n]{1,120})["']/g;
68
+ const FRONT_MATTER_ENTRY = /^( *)["']?([^"':\n]+?)["']?\s*:\s*(.*)$/;
69
+ const BASE_THEME_SELECTOR = String.raw`(?::root|:host|html|\.light)(?:\[data-theme=["']?light["']?\])?`;
70
+ const TOKEN_BLOCK_SELECTOR = new RegExp(`^${BASE_THEME_SELECTOR}(?:\\s*,\\s*${BASE_THEME_SELECTOR})*$`);
71
+ const CONFIG_COMMENT = /^\s*\/\*[\s\S]*?\*\/|^\s*\/\/.*$/gm;
72
+ const BASE_COLOR_NAME = /^[a-z]{1,16}$/;
73
+ const PACKAGE_SCOPE = /^(?:apps|packages)\/[^/]+/;
74
+ const CONTRACT_TOKEN_SECTIONS = new Set(["colors", "typography", "rounded", "spacing"]);
75
+
76
+ const FRAMEWORK_PACKAGES = [
77
+ "next", "react", "vue", "nuxt", "svelte", "@sveltejs/kit", "astro", "solid-js",
78
+ ];
79
+ const STYLING_PACKAGES = [
80
+ "styled-components", "@emotion/react", "@vanilla-extract/css", "@stitches/react",
81
+ "@mui/material", "@chakra-ui/react", "@mantine/core", "antd",
82
+ ];
83
+
84
+ // Values from shadcn/ui registry base colours (MIT) and create-next-app, as recorded in the
85
+ // skill's licence record. Stock values are not a brand, however many of them a repo declares.
86
+ const STOCK_TOKEN_VALUES = new Set([
87
+ "0 0% 100%", "0 0% 14.9%", "0 0% 3.9%", "0 0% 45.1%", "0 0% 63.9%", "0 0% 83.1%", "0 0% 89.8%",
88
+ "0 0% 9%", "0 0% 96.1%", "0 0% 98%", "0 62.8% 30.6%", "0 84.2% 60.2%", "12 6.5% 15.1%",
89
+ "12 76% 61%", "160 60% 45%", "173 58% 39%", "197 37% 24%", "20 14.3% 4.1%", "20 5.9% 90%",
90
+ "210 20% 98%", "210 40% 96.1%", "210 40% 98%", "212.7 26.8% 83.9%", "214.3 31.8% 91.4%",
91
+ "215 20.2% 65.1%", "215 27.9% 16.9%", "215.4 16.3% 46.9%", "216 12.2% 83.9%",
92
+ "217.2 32.6% 17.5%", "217.2 91.2% 59.8%", "217.9 10.6% 64.9%", "220 13% 91%", "220 14.3% 95.9%",
93
+ "220 70% 50%", "220 8.9% 46.1%", "220.9 39.3% 11%", "222.2 47.4% 11.2%", "222.2 84% 4.9%",
94
+ "224 71.4% 4.1%", "224.3 76.3% 48%", "24 5.4% 63.9%", "24 5.7% 82.9%", "24 9.8% 10%",
95
+ "240 10% 3.9%", "240 3.7% 15.9%", "240 3.8% 46.1%", "240 4.8% 95.9%", "240 4.9% 83.9%",
96
+ "240 5% 64.9%", "240 5.3% 26.1%", "240 5.9% 10%", "240 5.9% 90%", "25 5.3% 44.7%", "27 87% 67%",
97
+ "280 65% 60%", "30 80% 55%", "340 75% 55%", "43 74% 66%", "60 4.8% 95.9%", "60 9.1% 97.8%",
98
+ "oklch(0.129 0.042 264.695)", "oklch(0.13 0.028 261.692)", "oklch(0.141 0.005 285.823)",
99
+ "oklch(0.145 0 0)", "oklch(0.145 0.008 326)", "oklch(0.147 0.004 49.25)",
100
+ "oklch(0.147 0.004 49.3)", "oklch(0.148 0.004 228.8)", "oklch(0.153 0.006 107.1)",
101
+ "oklch(0.205 0 0)", "oklch(0.208 0.042 265.755)", "oklch(0.21 0.006 285.885)",
102
+ "oklch(0.21 0.034 264.665)", "oklch(0.212 0.019 322.12)", "oklch(0.214 0.009 43.1)",
103
+ "oklch(0.216 0.006 56.043)", "oklch(0.218 0.008 223.9)", "oklch(0.228 0.013 107.4)",
104
+ "oklch(0.263 0.024 320.12)", "oklch(0.268 0.007 34.298)", "oklch(0.268 0.011 36.5)",
105
+ "oklch(0.269 0 0)", "oklch(0.274 0.006 286.033)", "oklch(0.275 0.011 216.9)",
106
+ "oklch(0.278 0.033 256.848)", "oklch(0.279 0.041 260.031)", "oklch(0.286 0.016 107.4)",
107
+ "oklch(0.364 0.029 323.89)", "oklch(0.367 0.016 35.7)", "oklch(0.37 0.013 285.805)",
108
+ "oklch(0.371 0 0)", "oklch(0.374 0.01 67.558)", "oklch(0.378 0.015 216)",
109
+ "oklch(0.394 0.023 107.4)", "oklch(0.398 0.07 227.392)", "oklch(0.435 0.029 321.78)",
110
+ "oklch(0.438 0.017 39.3)", "oklch(0.439 0 0)", "oklch(0.442 0.017 285.786)",
111
+ "oklch(0.444 0.011 73.639)", "oklch(0.45 0.017 213.2)", "oklch(0.466 0.025 107.3)",
112
+ "oklch(0.488 0.243 264.376)", "oklch(0.542 0.034 322.5)", "oklch(0.547 0.021 43.1)",
113
+ "oklch(0.551 0.027 264.364)", "oklch(0.552 0.016 285.938)", "oklch(0.553 0.013 58.071)",
114
+ "oklch(0.554 0.046 257.417)", "oklch(0.556 0 0)", "oklch(0.56 0.021 213.5)",
115
+ "oklch(0.577 0.245 27.325)", "oklch(0.58 0.031 107.3)", "oklch(0.6 0.118 184.704)",
116
+ "oklch(0.627 0.265 303.9)", "oklch(0.645 0.246 16.439)", "oklch(0.646 0.222 41.116)",
117
+ "oklch(0.696 0.17 162.48)", "oklch(0.704 0.04 256.788)", "oklch(0.704 0.191 22.216)",
118
+ "oklch(0.705 0.015 286.067)", "oklch(0.707 0.022 261.325)", "oklch(0.708 0 0)",
119
+ "oklch(0.709 0.01 56.259)", "oklch(0.711 0.019 323.02)", "oklch(0.714 0.014 41.2)",
120
+ "oklch(0.723 0.014 214.4)", "oklch(0.737 0.021 106.9)", "oklch(0.769 0.188 70.08)",
121
+ "oklch(0.828 0.189 84.429)", "oklch(0.865 0.012 325.68)", "oklch(0.868 0.007 39.5)",
122
+ "oklch(0.869 0.005 56.366)", "oklch(0.87 0 0)", "oklch(0.871 0.006 286.286)",
123
+ "oklch(0.872 0.007 219.6)", "oklch(0.88 0.011 106.6)", "oklch(0.92 0.004 286.32)",
124
+ "oklch(0.922 0 0)", "oklch(0.922 0.005 325.62)", "oklch(0.922 0.005 34.3)",
125
+ "oklch(0.923 0.003 48.717)", "oklch(0.925 0.005 214.3)", "oklch(0.928 0.006 264.531)",
126
+ "oklch(0.929 0.013 255.508)", "oklch(0.93 0.007 106.5)", "oklch(0.96 0.002 17.2)",
127
+ "oklch(0.96 0.003 325.6)", "oklch(0.963 0.002 197.1)", "oklch(0.966 0.005 106.5)",
128
+ "oklch(0.967 0.001 286.375)", "oklch(0.967 0.003 264.542)", "oklch(0.968 0.007 247.896)",
129
+ "oklch(0.97 0 0)", "oklch(0.97 0.001 106.424)", "oklch(0.984 0.003 247.858)", "oklch(0.985 0 0)",
130
+ "oklch(0.985 0.001 106.423)", "oklch(0.985 0.002 247.839)", "oklch(0.986 0.002 67.8)",
131
+ "oklch(0.987 0.002 197.1)", "oklch(0.988 0.003 106.5)", "oklch(1 0 0 / 10%)",
132
+ "oklch(1 0 0 / 15%)", "oklch(1 0 0)", "#ffffff", "#171717", "#0a0a0a", "#ededed", "Geist",
133
+ "Geist Mono", "Inter",
134
+ ]);
135
+
136
+ function collectFiles(root) {
137
+ const files = [];
138
+ const queue = [""];
139
+ let truncated = false;
140
+ let unread = 0;
141
+ while (queue.length > 0 && !truncated) {
142
+ const directory = queue.shift();
143
+ let entries;
144
+ try {
145
+ entries = readdirSync(join(root, directory), { withFileTypes: true });
146
+ } catch {
147
+ unread += 1;
148
+ continue;
149
+ }
150
+ entries.sort((left, right) => (left.name < right.name ? -1 : 1));
151
+ for (const entry of entries) {
152
+ const path = directory ? `${directory}/${entry.name}` : entry.name;
153
+ if (!isSafePath(path)) {
154
+ unread += 1;
155
+ } else if (entry.isDirectory()) {
156
+ if (isScannedDirectory(entry.name)) queue.push(path);
157
+ } else if (entry.isFile()) {
158
+ if (files.length === MAX_FILES) {
159
+ truncated = true;
160
+ break;
161
+ }
162
+ files.push(path);
163
+ }
164
+ }
165
+ }
166
+ return { files, truncated, unread };
167
+ }
168
+
169
+ function isSafePath(path) {
170
+ return (
171
+ SAFE_PATH.test(path) &&
172
+ path.split(" ").length - 1 <= MAX_PATH_SPACES &&
173
+ path.split("/").every((segment) => segment.length <= MAX_PATH_SEGMENT_LENGTH)
174
+ );
175
+ }
176
+
177
+ function isScannedDirectory(name) {
178
+ if (name.startsWith(".")) return READABLE_HIDDEN_DIRECTORIES.has(name);
179
+ return !SKIPPED_DIRECTORIES.has(name);
180
+ }
181
+
182
+ function colorValue(raw) {
183
+ const value = raw.trim().toLowerCase().replace(/\s+/g, " ");
184
+ if (value.length > MAX_VALUE_LENGTH) return null;
185
+ if (HEX_COLOR.test(value)) return expandShortHex(value);
186
+ return COLOR_FUNCTION.test(value) || HSL_CHANNELS.test(value) ? value : null;
187
+ }
188
+
189
+ function expandShortHex(hex) {
190
+ if (hex.length > 5) return hex;
191
+ return `#${[...hex.slice(1)].map((digit) => digit + digit).join("")}`;
192
+ }
193
+
194
+ function dimensionValue(raw) {
195
+ const value = raw.trim().toLowerCase();
196
+ return DIMENSION.test(value) ? value : null;
197
+ }
198
+
199
+ function fontName(raw) {
200
+ const name = raw.trim().replace(/^["']|["']$/g, "").trim();
201
+ if (!FONT_NAME.test(name) || GENERIC_FONT.test(name)) return null;
202
+ return name.split(/[\s-]+/).length <= MAX_FONT_WORDS ? name : null;
203
+ }
204
+
205
+ function firstFontInStack(stack) {
206
+ return stack.length > MAX_VALUE_LENGTH ? null : fontName(stack.split(",")[0]);
207
+ }
208
+
209
+ function typedTokenValue(name, raw) {
210
+ const color = colorValue(raw);
211
+ if (color) return { value: color, kind: "color" };
212
+ const dimension = dimensionValue(raw);
213
+ if (dimension) return { value: dimension, kind: "dimension" };
214
+ if (!FONT_TOKEN_NAME.test(name)) return null;
215
+ const font = firstFontInStack(raw);
216
+ return font ? { value: font, kind: "font" } : null;
217
+ }
218
+
219
+ function parseCssStatements(text, hasLineComments) {
220
+ const statements = [];
221
+ const enclosingBlocks = [];
222
+ let buffer = "";
223
+ let line = 1;
224
+ let statementLine = 1;
225
+ let statementStarted = false;
226
+ let quote = null;
227
+
228
+ const flush = () => {
229
+ if (statementStarted) statements.push({ text: buffer.trim(), line: statementLine, blocks: [...enclosingBlocks] });
230
+ buffer = "";
231
+ statementStarted = false;
232
+ };
233
+
234
+ for (let index = 0; index < text.length; index += 1) {
235
+ const character = text[index];
236
+ if (quote) {
237
+ buffer += character;
238
+ if (character === "\n") {
239
+ line += 1;
240
+ quote = null;
241
+ } else if (character === "\\") {
242
+ index += 1;
243
+ buffer += text[index] ?? "";
244
+ } else if (character === quote) {
245
+ quote = null;
246
+ }
247
+ } else if (character === "/" && text[index + 1] === "*") {
248
+ const close = text.indexOf("*/", index + 2);
249
+ const end = close === -1 ? text.length : close + 2;
250
+ line += text.slice(index, end).split("\n").length - 1;
251
+ index = end - 1;
252
+ } else if (
253
+ hasLineComments &&
254
+ character === "/" &&
255
+ text[index + 1] === "/" &&
256
+ (index === 0 || /\s/.test(text[index - 1]))
257
+ ) {
258
+ const lineEnd = text.indexOf("\n", index);
259
+ index = (lineEnd === -1 ? text.length : lineEnd) - 1;
260
+ } else if (character === "{") {
261
+ if (enclosingBlocks.length === MAX_CSS_DEPTH) return statements;
262
+ enclosingBlocks.push(buffer.trim());
263
+ buffer = "";
264
+ statementStarted = false;
265
+ } else if (character === ";" || character === "}") {
266
+ flush();
267
+ if (character === "}") enclosingBlocks.pop();
268
+ } else {
269
+ if (character === "\n") line += 1;
270
+ if (!statementStarted && !/\s/.test(character)) {
271
+ statementStarted = true;
272
+ statementLine = line;
273
+ }
274
+ if (character === '"' || character === "'") quote = character;
275
+ buffer += character;
276
+ }
277
+ }
278
+ flush();
279
+ return statements;
280
+ }
281
+
282
+ function splitDeclaration(statement) {
283
+ const colon = statement.indexOf(":");
284
+ if (colon === -1) return null;
285
+ return { property: statement.slice(0, colon).trim(), value: statement.slice(colon + 1).trim() };
286
+ }
287
+
288
+ function isTokenBlock(blocks) {
289
+ const innermost = blocks.at(-1) ?? "";
290
+ const isThemeOrRoot = /^@theme\b/.test(innermost) || TOKEN_BLOCK_SELECTOR.test(innermost);
291
+ return isThemeOrRoot && blocks.slice(0, -1).every((block) => block.startsWith("@layer"));
292
+ }
293
+
294
+ function createFindings() {
295
+ return { tokens: [], brandValues: [], tokensCapped: false, fonts: new Map(), colors: new Map(), radii: new Map() };
296
+ }
297
+
298
+ function countValue(counts, value) {
299
+ counts.set(value, (counts.get(value) ?? 0) + 1);
300
+ }
301
+
302
+ function recordFont(findings, family, source) {
303
+ const existing = findings.fonts.get(family);
304
+ if (existing) existing.count += 1;
305
+ else findings.fonts.set(family, { family, source, count: 1 });
306
+ }
307
+
308
+ function recordToken(findings, name, raw, source, line) {
309
+ if (!SAFE_NAME.test(name.replaceAll(".", "-"))) return;
310
+ const typed = typedTokenValue(name, raw);
311
+ if (!typed) return;
312
+ if (findings.tokens.length === MAX_TOKENS) {
313
+ findings.tokensCapped = true;
314
+ return;
315
+ }
316
+ findings.tokens.push({ name, value: typed.value, source, line });
317
+ if (typed.kind !== "dimension") findings.brandValues.push(typed.value);
318
+ if (typed.kind === "font") recordFont(findings, typed.value, source);
319
+ }
320
+
321
+ function countColorsIn(findings, text) {
322
+ for (const [match] of text.matchAll(HEX_IN_TEXT)) {
323
+ const color = colorValue(match);
324
+ if (color) countValue(findings.colors, color);
325
+ }
326
+ for (const [match] of text.matchAll(COLOR_FUNCTION_IN_TEXT)) {
327
+ const color = colorValue(match);
328
+ if (color) countValue(findings.colors, color);
329
+ }
330
+ }
331
+
332
+ function recordFontImportsIn(findings, text, source) {
333
+ for (const [, names] of text.matchAll(NEXT_FONT_IMPORT)) {
334
+ for (const part of names.split(",")) {
335
+ const font = fontName(part.split(/\sas\s/)[0].replaceAll("_", " "));
336
+ if (font) recordFont(findings, font, source);
337
+ }
338
+ }
339
+ for (const [, slug] of text.matchAll(FONTSOURCE_IMPORT)) {
340
+ const words = slug.split("-").filter(Boolean).map((word) => word[0].toUpperCase() + word.slice(1));
341
+ const font = fontName(words.join(" "));
342
+ if (font) recordFont(findings, font, source);
343
+ }
344
+ for (const [url] of text.matchAll(GOOGLE_FONTS_URL)) {
345
+ for (const [, family] of url.matchAll(GOOGLE_FONT_FAMILY)) {
346
+ const font = fontName(family.replaceAll("+", " "));
347
+ if (font) recordFont(findings, font, source);
348
+ }
349
+ }
350
+ }
351
+
352
+ function scanStylesheet(findings, statements, source) {
353
+ for (const { text, line, blocks } of statements) {
354
+ if (text.startsWith("@")) {
355
+ recordFontImportsIn(findings, text, source);
356
+ continue;
357
+ }
358
+ const declaration = splitDeclaration(text);
359
+ if (!declaration) continue;
360
+ const { property, value } = declaration;
361
+ if (property.startsWith("--") && isTokenBlock(blocks)) {
362
+ recordToken(findings, property, value, source, line);
363
+ }
364
+ if (property === "font-family") {
365
+ const font = firstFontInStack(value);
366
+ if (font) recordFont(findings, font, source);
367
+ }
368
+ if (property === "border-radius" || property.startsWith("--radius")) {
369
+ for (const part of value.split(/\s+/)) {
370
+ const radius = dimensionValue(part);
371
+ if (radius) countValue(findings.radii, radius);
372
+ }
373
+ }
374
+ countColorsIn(findings, value);
375
+ }
376
+ }
377
+
378
+ function scanCode(findings, text, source) {
379
+ countColorsIn(findings, text);
380
+ recordFontImportsIn(findings, text, source);
381
+ for (const [match] of text.matchAll(ROUNDED_CLASS)) countValue(findings.radii, match);
382
+ for (const pattern of [ROUNDED_ARBITRARY_CLASS, JS_BORDER_RADIUS]) {
383
+ for (const [, raw] of text.matchAll(pattern)) {
384
+ const radius = dimensionValue(raw);
385
+ if (radius) countValue(findings.radii, radius);
386
+ }
387
+ }
388
+ }
389
+
390
+ function blankOutComment(comment) {
391
+ return comment.replace(/[^\n]/g, " ");
392
+ }
393
+
394
+ function scanTailwindConfig(findings, rawText, source) {
395
+ const text = rawText.replace(CONFIG_COMMENT, blankOutComment);
396
+ let line = 1;
397
+ let countedUpTo = 0;
398
+ for (const match of text.matchAll(CONFIG_STRING_ENTRY)) {
399
+ for (; countedUpTo < match.index; countedUpTo += 1) {
400
+ if (text[countedUpTo] === "\n") line += 1;
401
+ }
402
+ const [, , name, raw] = match;
403
+ if (colorValue(raw)) recordToken(findings, name, raw, source, line);
404
+ }
405
+ for (const [, block] of text.matchAll(CONFIG_FONT_BLOCK)) {
406
+ for (const [, , , family] of block.matchAll(CONFIG_FONT_ENTRY)) {
407
+ const font = fontName(family);
408
+ if (font) recordFont(findings, font, source);
409
+ }
410
+ }
411
+ scanCode(findings, text, source);
412
+ }
413
+
414
+ function scanContractFrontMatter(findings, text, source) {
415
+ const lines = text.split(/\r?\n/);
416
+ if (lines[0].trim() !== "---") return;
417
+ const path = [];
418
+ for (let index = 1; index < lines.length && lines[index].trim() !== "---"; index += 1) {
419
+ if (lines[index].length > MAX_FRONT_MATTER_LINE_LENGTH) continue;
420
+ const entry = FRONT_MATTER_ENTRY.exec(lines[index]);
421
+ if (!entry || !SAFE_NAME.test(entry[2])) continue;
422
+ const [, indent, key, rawValue] = entry;
423
+ while (path.length > 0 && path.at(-1).indent >= indent.length) path.pop();
424
+ path.push({ indent: indent.length, key });
425
+ if (!CONTRACT_TOKEN_SECTIONS.has(path[0].key)) continue;
426
+ const value = rawValue.replace(/\s+#.*$/, "").trim().replace(/^["']|["']$/g, "");
427
+ if (value) recordToken(findings, path.map((part) => part.key).join("."), value, source, index + 1);
428
+ }
429
+ }
430
+
431
+ function parseJsonObject(text) {
432
+ try {
433
+ const value = JSON.parse(text);
434
+ return value && typeof value === "object" ? value : {};
435
+ } catch {
436
+ return {};
437
+ }
438
+ }
439
+
440
+ function recordDependencies(dependencies, packageJson) {
441
+ for (const group of [packageJson.dependencies, packageJson.devDependencies]) {
442
+ for (const [name, version] of Object.entries(group ?? {})) {
443
+ if (typeof version === "string" && !dependencies.has(name)) dependencies.set(name, version);
444
+ }
445
+ }
446
+ }
447
+
448
+ function tailwindMajorFromVersion(version) {
449
+ const match = /^[\^~>=<\s]*(\d+)/.exec(version ?? "");
450
+ return match ? Number(match[1]) : null;
451
+ }
452
+
453
+ function nearestTailwindMajor(path, tailwindVersions) {
454
+ let directory = dirname(path);
455
+ for (;;) {
456
+ const major = tailwindMajorFromVersion(tailwindVersions.get(directory));
457
+ if (major !== null) return major;
458
+ if (directory === ".") return null;
459
+ directory = dirname(directory);
460
+ }
461
+ }
462
+
463
+ function detectTailwind(root, files, stylesheets, tailwindVersions) {
464
+ const scannedFiles = new Set(files);
465
+ const configs = files.filter((path) => TAILWIND_CONFIG_FILE.test(path));
466
+ const loadedConfigs = new Set();
467
+ const themeFiles = [];
468
+ let hasV3Directive = false;
469
+ let hasV4Signal = false;
470
+ for (const [source, statements] of stylesheets) {
471
+ for (const { text } of statements) {
472
+ if (/^@tailwind\b/.test(text)) hasV3Directive = true;
473
+ if (/^@import\s+["']tailwindcss/.test(text)) hasV4Signal = true;
474
+ const loaded = /^@config\s+["']([^"']+)["']/.exec(text);
475
+ if (loaded) {
476
+ hasV4Signal = true;
477
+ const target = relative(root, resolve(root, dirname(source), loaded[1])).split(sep).join("/");
478
+ if (scannedFiles.has(target) && CODE_FILE.test(target)) loadedConfigs.add(target);
479
+ }
480
+ }
481
+ if (statements.some(({ blocks }) => /^@theme\b/.test(blocks.at(-1) ?? ""))) {
482
+ hasV4Signal = true;
483
+ themeFiles.push(source);
484
+ }
485
+ }
486
+ const declaredMajor = tailwindMajorFromVersion([...tailwindVersions.values()][0]);
487
+ const inferredMajor = hasV3Directive ? 3 : hasV4Signal ? 4 : null;
488
+ const major = declaredMajor ?? inferredMajor;
489
+ if (major === null && configs.length === 0) return null;
490
+ const isLiveConfig = (path) =>
491
+ loadedConfigs.has(path) || (nearestTailwindMajor(path, tailwindVersions) ?? inferredMajor) === 3;
492
+ const activeConfigs = [
493
+ ...configs.filter(isLiveConfig),
494
+ ...[...loadedConfigs].filter((path) => !configs.includes(path)),
495
+ ];
496
+ return {
497
+ major,
498
+ themeFiles,
499
+ activeConfigs,
500
+ staleConfigs: configs.filter((path) => !activeConfigs.includes(path)),
501
+ };
502
+ }
503
+
504
+ function detectShadcn(componentsJson, files) {
505
+ if (!componentsJson) return null;
506
+ const tailwind = componentsJson.tailwind ?? {};
507
+ return {
508
+ css: files.includes(tailwind.css) ? tailwind.css : null,
509
+ baseColor: BASE_COLOR_NAME.test(tailwind.baseColor) ? tailwind.baseColor : null,
510
+ cssVariables: tailwind.cssVariables === true,
511
+ };
512
+ }
513
+
514
+ function canonicalTokenName(name) {
515
+ return name
516
+ .replace(/^--/, "")
517
+ .replace(/^colors?[.-]/, "")
518
+ .replace(/^rounded\./, "radius-")
519
+ .replaceAll(".", "-");
520
+ }
521
+
522
+ function packageScope(source) {
523
+ return PACKAGE_SCOPE.exec(source)?.[0] ?? "";
524
+ }
525
+
526
+ function disagreesWithinOneScope(tokens) {
527
+ return [...new Set(tokens.map(({ source }) => packageScope(source)))].some((scope) => {
528
+ const visible = tokens.filter(({ source }) => ["", scope].includes(packageScope(source)));
529
+ return (
530
+ new Set(visible.map(({ value }) => value)).size > 1 &&
531
+ new Set(visible.map(({ source }) => source)).size > 1
532
+ );
533
+ });
534
+ }
535
+
536
+ function findConflicts(tokens) {
537
+ const tokensByName = new Map();
538
+ for (const token of tokens) {
539
+ const name = canonicalTokenName(token.name);
540
+ tokensByName.set(name, [...(tokensByName.get(name) ?? []), token]);
541
+ }
542
+ const conflicts = [];
543
+ for (const [name, named] of tokensByName) {
544
+ if (!disagreesWithinOneScope(named)) continue;
545
+ const firstByValue = new Map();
546
+ for (const { value, source, line } of named) {
547
+ if (!firstByValue.has(value)) firstByValue.set(value, { value, source: `${source}:${line}` });
548
+ }
549
+ conflicts.push({ token: name, values: [...firstByValue.values()] });
550
+ }
551
+ return conflicts;
552
+ }
553
+
554
+ function stockComparisonKey(value) {
555
+ const wrappedHsl = /^hsla?\((.*)\)$/.exec(value);
556
+ return wrappedHsl ? wrappedHsl[1].replaceAll(",", " ").replace(/\s+/g, " ").trim() : value;
557
+ }
558
+
559
+ function brandStatus({ tokens, brandValues }) {
560
+ if (tokens.length === 0) return "none";
561
+ return brandValues.every((value) => STOCK_TOKEN_VALUES.has(stockComparisonKey(value))) ? "default" : "real";
562
+ }
563
+
564
+ function topFrequencies(counts) {
565
+ return [...counts]
566
+ .map(([value, count]) => ({ value, count }))
567
+ .sort((left, right) => right.count - left.count)
568
+ .slice(0, TOP_FREQUENCIES);
569
+ }
570
+
571
+ function createInventory(root) {
572
+ const { files, truncated, unread: unreadDirectoryEntries } = collectFiles(root);
573
+ let unread = unreadDirectoryEntries;
574
+ let bytesRead = 0;
575
+ let outOfBytes = false;
576
+
577
+ const readText = (path) => {
578
+ try {
579
+ const fullPath = join(root, path);
580
+ const { size } = statSync(fullPath);
581
+ outOfBytes ||= bytesRead + size > MAX_TOTAL_BYTES;
582
+ if (size <= MAX_FILE_BYTES && !outOfBytes) {
583
+ bytesRead += size;
584
+ return readFileSync(fullPath, "utf8");
585
+ }
586
+ } catch {}
587
+ unread += 1;
588
+ return null;
589
+ };
590
+
591
+ const dependencies = new Map();
592
+ const tailwindVersions = new Map();
593
+ let componentsJson = null;
594
+ const stylesheets = new Map();
595
+ for (const path of files) {
596
+ const fileName = path.split("/").at(-1);
597
+ if (fileName === "package.json") {
598
+ const text = readText(path);
599
+ if (text === null) continue;
600
+ const packageJson = parseJsonObject(text);
601
+ recordDependencies(dependencies, packageJson);
602
+ const version = packageJson.dependencies?.tailwindcss ?? packageJson.devDependencies?.tailwindcss;
603
+ if (typeof version === "string") tailwindVersions.set(dirname(path), version);
604
+ } else if (path === "components.json") {
605
+ const text = readText(path);
606
+ if (text !== null) componentsJson = parseJsonObject(text);
607
+ } else if (STYLE_FILE.test(path) && !MINIFIED_FILE.test(path) && !BUILT_STYLE_FILE.test(path)) {
608
+ const text = readText(path);
609
+ if (text !== null && !TAILWIND_BUILD_HEADER.test(text) && !TAILWIND_COMPILED_THEME.test(text)) {
610
+ stylesheets.set(path, parseCssStatements(text, !path.endsWith(".css")));
611
+ }
612
+ }
613
+ }
614
+
615
+ const tailwind = detectTailwind(root, files, stylesheets, tailwindVersions);
616
+ const findings = createFindings();
617
+ for (const path of files) {
618
+ if (tailwind?.activeConfigs.includes(path)) {
619
+ const text = readText(path);
620
+ if (text !== null) scanTailwindConfig(findings, text, path);
621
+ } else if (stylesheets.has(path)) {
622
+ scanStylesheet(findings, stylesheets.get(path), path);
623
+ } else if (DESIGN_CONTRACT_FILE.test(path)) {
624
+ const text = readText(path);
625
+ if (text !== null) scanContractFrontMatter(findings, text, path);
626
+ } else if (CODE_FILE.test(path) && !MINIFIED_FILE.test(path) && !TAILWIND_CONFIG_FILE.test(path)) {
627
+ const text = readText(path);
628
+ if (text !== null) scanCode(findings, text, path);
629
+ }
630
+ }
631
+
632
+ const styling = STYLING_PACKAGES.filter((name) => dependencies.has(name));
633
+ if (files.some((path) => /\.module\.(?:css|scss)$/.test(path))) styling.push("css-modules");
634
+
635
+ const fonts = [...findings.fonts.values()];
636
+ const logos = files.filter((path) => LOGO_FILE.test(path));
637
+ const contracts = files.filter((path) => CONTRACT_FILE.test(path));
638
+ const listIsCapped = [fonts, logos, contracts].some((list) => list.length > MAX_LISTED_ITEMS);
639
+
640
+ return {
641
+ stack: {
642
+ frameworks: FRAMEWORK_PACKAGES.filter((name) => dependencies.has(name)),
643
+ styling,
644
+ tailwind: tailwind && {
645
+ major: tailwind.major,
646
+ themeFiles: tailwind.themeFiles,
647
+ activeConfigs: tailwind.activeConfigs,
648
+ staleConfigs: tailwind.staleConfigs,
649
+ },
650
+ shadcn: detectShadcn(componentsJson, files),
651
+ },
652
+ tokens: findings.tokens,
653
+ fonts: fonts.slice(0, MAX_LISTED_ITEMS),
654
+ logos: logos.slice(0, MAX_LISTED_ITEMS),
655
+ contracts: contracts.slice(0, MAX_LISTED_ITEMS),
656
+ frequencies: { colors: topFrequencies(findings.colors), radii: topFrequencies(findings.radii) },
657
+ brandStatus: brandStatus(findings),
658
+ conflicts: findConflicts(findings.tokens),
659
+ scan: { files: files.length, truncated: truncated || outOfBytes || findings.tokensCapped || listIsCapped, unread },
660
+ };
661
+ }
662
+
663
+ function isDirectory(path) {
664
+ try {
665
+ return statSync(path).isDirectory();
666
+ } catch {
667
+ return false;
668
+ }
669
+ }
670
+
671
+ const root = resolve(process.argv[2] ?? ".");
672
+ if (!isDirectory(root)) {
673
+ process.stderr.write("inventory: the root is not a directory\n");
674
+ process.exit(EXIT_BAD_ROOT);
675
+ }
676
+ process.stdout.write(`${JSON.stringify(createInventory(root), null, 2)}\n`);
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Eduardo Calvo
3
+ Copyright (c) 2025 Vercel Labs
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal