@react-spa-scaffold/mcp 1.1.3 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (261) hide show
  1. package/README.md +19 -15
  2. package/dist/constants.d.ts +35 -0
  3. package/dist/constants.d.ts.map +1 -0
  4. package/dist/constants.js +35 -0
  5. package/dist/constants.js.map +1 -0
  6. package/dist/features/definitions/api.d.ts +3 -0
  7. package/dist/features/definitions/api.d.ts.map +1 -0
  8. package/dist/features/definitions/api.js +20 -0
  9. package/dist/features/definitions/api.js.map +1 -0
  10. package/dist/features/definitions/ci.d.ts +3 -0
  11. package/dist/features/definitions/ci.d.ts.map +1 -0
  12. package/dist/features/definitions/ci.js +25 -0
  13. package/dist/features/definitions/ci.js.map +1 -0
  14. package/dist/features/definitions/core.d.ts +3 -0
  15. package/dist/features/definitions/core.d.ts.map +1 -0
  16. package/dist/features/definitions/core.js +76 -0
  17. package/dist/features/definitions/core.js.map +1 -0
  18. package/dist/features/definitions/devtools.d.ts +3 -0
  19. package/dist/features/definitions/devtools.d.ts.map +1 -0
  20. package/dist/features/definitions/devtools.js +53 -0
  21. package/dist/features/definitions/devtools.js.map +1 -0
  22. package/dist/features/definitions/forms.d.ts +3 -0
  23. package/dist/features/definitions/forms.d.ts.map +1 -0
  24. package/dist/features/definitions/forms.js +32 -0
  25. package/dist/features/definitions/forms.js.map +1 -0
  26. package/dist/features/definitions/i18n.d.ts +3 -0
  27. package/dist/features/definitions/i18n.d.ts.map +1 -0
  28. package/dist/features/definitions/i18n.js +50 -0
  29. package/dist/features/definitions/i18n.js.map +1 -0
  30. package/dist/features/definitions/index.d.ts +15 -0
  31. package/dist/features/definitions/index.d.ts.map +1 -0
  32. package/dist/features/definitions/index.js +15 -0
  33. package/dist/features/definitions/index.js.map +1 -0
  34. package/dist/features/definitions/mobile.d.ts +3 -0
  35. package/dist/features/definitions/mobile.d.ts.map +1 -0
  36. package/dist/features/definitions/mobile.js +19 -0
  37. package/dist/features/definitions/mobile.js.map +1 -0
  38. package/dist/features/definitions/observability.d.ts +3 -0
  39. package/dist/features/definitions/observability.d.ts.map +1 -0
  40. package/dist/features/definitions/observability.js +21 -0
  41. package/dist/features/definitions/observability.js.map +1 -0
  42. package/dist/features/definitions/performance.d.ts +3 -0
  43. package/dist/features/definitions/performance.d.ts.map +1 -0
  44. package/dist/features/definitions/performance.js +28 -0
  45. package/dist/features/definitions/performance.js.map +1 -0
  46. package/dist/features/definitions/routing.d.ts +3 -0
  47. package/dist/features/definitions/routing.d.ts.map +1 -0
  48. package/dist/features/definitions/routing.js +25 -0
  49. package/dist/features/definitions/routing.js.map +1 -0
  50. package/dist/features/definitions/state.d.ts +3 -0
  51. package/dist/features/definitions/state.d.ts.map +1 -0
  52. package/dist/features/definitions/state.js +27 -0
  53. package/dist/features/definitions/state.js.map +1 -0
  54. package/dist/features/definitions/testing.d.ts +3 -0
  55. package/dist/features/definitions/testing.d.ts.map +1 -0
  56. package/dist/features/definitions/testing.js +56 -0
  57. package/dist/features/definitions/testing.js.map +1 -0
  58. package/dist/features/definitions/theming.d.ts +3 -0
  59. package/dist/features/definitions/theming.d.ts.map +1 -0
  60. package/dist/features/definitions/theming.js +27 -0
  61. package/dist/features/definitions/theming.js.map +1 -0
  62. package/dist/features/definitions/ui.d.ts +3 -0
  63. package/dist/features/definitions/ui.d.ts.map +1 -0
  64. package/dist/features/definitions/ui.js +44 -0
  65. package/dist/features/definitions/ui.js.map +1 -0
  66. package/dist/features/index.d.ts +3 -2
  67. package/dist/features/index.d.ts.map +1 -1
  68. package/dist/features/index.js +2 -1
  69. package/dist/features/index.js.map +1 -1
  70. package/dist/features/registry.d.ts +3 -9
  71. package/dist/features/registry.d.ts.map +1 -1
  72. package/dist/features/registry.js +3 -554
  73. package/dist/features/registry.js.map +1 -1
  74. package/dist/features/types.d.ts +17 -20
  75. package/dist/features/types.d.ts.map +1 -1
  76. package/dist/features/types.js +7 -1
  77. package/dist/features/types.js.map +1 -1
  78. package/dist/features/types.test.d.ts +5 -0
  79. package/dist/features/types.test.d.ts.map +1 -0
  80. package/dist/features/types.test.js +32 -0
  81. package/dist/features/types.test.js.map +1 -0
  82. package/dist/resources/docs.d.ts +2 -8
  83. package/dist/resources/docs.d.ts.map +1 -1
  84. package/dist/resources/docs.js +27 -49
  85. package/dist/resources/docs.js.map +1 -1
  86. package/dist/server.d.ts +2 -7
  87. package/dist/server.d.ts.map +1 -1
  88. package/dist/server.js +24 -57
  89. package/dist/server.js.map +1 -1
  90. package/dist/server.test.d.ts +2 -0
  91. package/dist/server.test.d.ts.map +1 -0
  92. package/dist/server.test.js +14 -0
  93. package/dist/server.test.js.map +1 -0
  94. package/dist/tools/get-example.d.ts +4 -15
  95. package/dist/tools/get-example.d.ts.map +1 -1
  96. package/dist/tools/get-example.js +3 -11
  97. package/dist/tools/get-example.js.map +1 -1
  98. package/dist/tools/get-example.test.d.ts +5 -0
  99. package/dist/tools/get-example.test.d.ts.map +1 -0
  100. package/dist/tools/get-example.test.js +63 -0
  101. package/dist/tools/get-example.test.js.map +1 -0
  102. package/dist/tools/get-features.d.ts +15 -14
  103. package/dist/tools/get-features.d.ts.map +1 -1
  104. package/dist/tools/get-features.js +12 -2
  105. package/dist/tools/get-features.js.map +1 -1
  106. package/dist/tools/get-features.test.d.ts +5 -0
  107. package/dist/tools/get-features.test.d.ts.map +1 -0
  108. package/dist/tools/get-features.test.js +45 -0
  109. package/dist/tools/get-features.test.js.map +1 -0
  110. package/dist/tools/get-file.d.ts +23 -0
  111. package/dist/tools/get-file.d.ts.map +1 -0
  112. package/dist/tools/get-file.js +53 -0
  113. package/dist/tools/get-file.js.map +1 -0
  114. package/dist/tools/get-file.test.d.ts +5 -0
  115. package/dist/tools/get-file.test.d.ts.map +1 -0
  116. package/dist/tools/get-file.test.js +63 -0
  117. package/dist/tools/get-file.test.js.map +1 -0
  118. package/dist/tools/get-scaffold.d.ts +8 -29
  119. package/dist/tools/get-scaffold.d.ts.map +1 -1
  120. package/dist/tools/get-scaffold.js +18 -24
  121. package/dist/tools/get-scaffold.js.map +1 -1
  122. package/dist/tools/get-scaffold.test.d.ts +5 -0
  123. package/dist/tools/get-scaffold.test.d.ts.map +1 -0
  124. package/dist/tools/get-scaffold.test.js +197 -0
  125. package/dist/tools/get-scaffold.test.js.map +1 -0
  126. package/dist/tools/index.d.ts +2 -0
  127. package/dist/tools/index.d.ts.map +1 -1
  128. package/dist/tools/index.js +1 -0
  129. package/dist/tools/index.js.map +1 -1
  130. package/dist/tools/registry.d.ts +13 -0
  131. package/dist/tools/registry.d.ts.map +1 -0
  132. package/dist/tools/registry.js +47 -0
  133. package/dist/tools/registry.js.map +1 -0
  134. package/dist/tools/types.d.ts +41 -0
  135. package/dist/tools/types.d.ts.map +1 -0
  136. package/dist/tools/types.js +8 -0
  137. package/dist/tools/types.js.map +1 -0
  138. package/dist/utils/cache.d.ts +25 -0
  139. package/dist/utils/cache.d.ts.map +1 -0
  140. package/dist/utils/cache.js +61 -0
  141. package/dist/utils/cache.js.map +1 -0
  142. package/dist/utils/docs.d.ts +1 -3
  143. package/dist/utils/docs.d.ts.map +1 -1
  144. package/dist/utils/docs.js +3 -8
  145. package/dist/utils/docs.js.map +1 -1
  146. package/dist/utils/docs.test.d.ts +5 -0
  147. package/dist/utils/docs.test.d.ts.map +1 -0
  148. package/dist/utils/docs.test.js +37 -0
  149. package/dist/utils/docs.test.js.map +1 -0
  150. package/dist/utils/errors.d.ts +8 -0
  151. package/dist/utils/errors.d.ts.map +1 -0
  152. package/dist/utils/errors.js +19 -0
  153. package/dist/utils/errors.js.map +1 -0
  154. package/dist/utils/examples/api-patterns.d.ts +3 -0
  155. package/dist/utils/examples/api-patterns.d.ts.map +1 -0
  156. package/dist/utils/examples/api-patterns.js +19 -0
  157. package/dist/utils/examples/api-patterns.js.map +1 -0
  158. package/dist/utils/examples/component-patterns.d.ts +3 -0
  159. package/dist/utils/examples/component-patterns.d.ts.map +1 -0
  160. package/dist/utils/examples/component-patterns.js +71 -0
  161. package/dist/utils/examples/component-patterns.js.map +1 -0
  162. package/dist/utils/examples/context-patterns.d.ts +3 -0
  163. package/dist/utils/examples/context-patterns.d.ts.map +1 -0
  164. package/dist/utils/examples/context-patterns.js +32 -0
  165. package/dist/utils/examples/context-patterns.js.map +1 -0
  166. package/dist/utils/examples/hook-patterns.d.ts +3 -0
  167. package/dist/utils/examples/hook-patterns.d.ts.map +1 -0
  168. package/dist/utils/examples/hook-patterns.js +55 -0
  169. package/dist/utils/examples/hook-patterns.js.map +1 -0
  170. package/dist/utils/examples/i18n-patterns.d.ts +3 -0
  171. package/dist/utils/examples/i18n-patterns.d.ts.map +1 -0
  172. package/dist/utils/examples/i18n-patterns.js +43 -0
  173. package/dist/utils/examples/i18n-patterns.js.map +1 -0
  174. package/dist/utils/examples/index.d.ts +12 -0
  175. package/dist/utils/examples/index.d.ts.map +1 -0
  176. package/dist/utils/examples/index.js +65 -0
  177. package/dist/utils/examples/index.js.map +1 -0
  178. package/dist/utils/examples/mobile-patterns.d.ts +3 -0
  179. package/dist/utils/examples/mobile-patterns.d.ts.map +1 -0
  180. package/dist/utils/examples/mobile-patterns.js +38 -0
  181. package/dist/utils/examples/mobile-patterns.js.map +1 -0
  182. package/dist/utils/examples/page-patterns.d.ts +3 -0
  183. package/dist/utils/examples/page-patterns.d.ts.map +1 -0
  184. package/dist/utils/examples/page-patterns.js +34 -0
  185. package/dist/utils/examples/page-patterns.js.map +1 -0
  186. package/dist/utils/examples/patterns.test.d.ts +6 -0
  187. package/dist/utils/examples/patterns.test.d.ts.map +1 -0
  188. package/dist/utils/examples/patterns.test.js +75 -0
  189. package/dist/utils/examples/patterns.test.js.map +1 -0
  190. package/dist/utils/examples/store-patterns.d.ts +3 -0
  191. package/dist/utils/examples/store-patterns.d.ts.map +1 -0
  192. package/dist/utils/examples/store-patterns.js +40 -0
  193. package/dist/utils/examples/store-patterns.js.map +1 -0
  194. package/dist/utils/examples/test-patterns.d.ts +3 -0
  195. package/dist/utils/examples/test-patterns.d.ts.map +1 -0
  196. package/dist/utils/examples/test-patterns.js +58 -0
  197. package/dist/utils/examples/test-patterns.js.map +1 -0
  198. package/dist/utils/examples/types.d.ts +17 -0
  199. package/dist/utils/examples/types.d.ts.map +1 -0
  200. package/dist/utils/examples/types.js +2 -0
  201. package/dist/utils/examples/types.js.map +1 -0
  202. package/dist/utils/examples/utility-patterns.d.ts +3 -0
  203. package/dist/utils/examples/utility-patterns.d.ts.map +1 -0
  204. package/dist/utils/examples/utility-patterns.js +77 -0
  205. package/dist/utils/examples/utility-patterns.js.map +1 -0
  206. package/dist/utils/index.d.ts +5 -3
  207. package/dist/utils/index.d.ts.map +1 -1
  208. package/dist/utils/index.js +5 -3
  209. package/dist/utils/index.js.map +1 -1
  210. package/dist/utils/paths.d.ts +13 -23
  211. package/dist/utils/paths.d.ts.map +1 -1
  212. package/dist/utils/paths.js +28 -39
  213. package/dist/utils/paths.js.map +1 -1
  214. package/dist/utils/paths.test.d.ts +2 -0
  215. package/dist/utils/paths.test.d.ts.map +1 -0
  216. package/dist/utils/paths.test.js +38 -0
  217. package/dist/utils/paths.test.js.map +1 -0
  218. package/dist/utils/scaffold/claude-md/index.d.ts +7 -0
  219. package/dist/utils/scaffold/claude-md/index.d.ts.map +1 -0
  220. package/dist/utils/scaffold/claude-md/index.js +23 -0
  221. package/dist/utils/scaffold/claude-md/index.js.map +1 -0
  222. package/dist/utils/scaffold/claude-md/sections.d.ts +16 -0
  223. package/dist/utils/scaffold/claude-md/sections.d.ts.map +1 -0
  224. package/dist/utils/scaffold/claude-md/sections.js +269 -0
  225. package/dist/utils/scaffold/claude-md/sections.js.map +1 -0
  226. package/dist/utils/scaffold/commands.d.ts +7 -0
  227. package/dist/utils/scaffold/commands.d.ts.map +1 -0
  228. package/dist/utils/scaffold/commands.js +19 -0
  229. package/dist/utils/scaffold/commands.js.map +1 -0
  230. package/dist/utils/scaffold/compute.d.ts +15 -0
  231. package/dist/utils/scaffold/compute.d.ts.map +1 -0
  232. package/dist/utils/scaffold/compute.js +63 -0
  233. package/dist/utils/scaffold/compute.js.map +1 -0
  234. package/dist/utils/scaffold/dependencies.d.ts +25 -0
  235. package/dist/utils/scaffold/dependencies.d.ts.map +1 -0
  236. package/dist/utils/scaffold/dependencies.js +101 -0
  237. package/dist/utils/scaffold/dependencies.js.map +1 -0
  238. package/dist/utils/scaffold/file-structure.d.ts +23 -0
  239. package/dist/utils/scaffold/file-structure.d.ts.map +1 -0
  240. package/dist/utils/scaffold/file-structure.js +62 -0
  241. package/dist/utils/scaffold/file-structure.js.map +1 -0
  242. package/dist/utils/scaffold/generators.d.ts +9 -0
  243. package/dist/utils/scaffold/generators.d.ts.map +1 -0
  244. package/dist/utils/scaffold/generators.js +121 -0
  245. package/dist/utils/scaffold/generators.js.map +1 -0
  246. package/dist/utils/scaffold/index.d.ts +10 -0
  247. package/dist/utils/scaffold/index.d.ts.map +1 -0
  248. package/dist/utils/scaffold/index.js +9 -0
  249. package/dist/utils/scaffold/index.js.map +1 -0
  250. package/package.json +2 -2
  251. package/templates/.github/workflows/release.yml +1 -1
  252. package/templates/e2e/tests/home.spec.ts +2 -2
  253. package/templates/e2e/tests/language.spec.ts +7 -3
  254. package/dist/utils/examples.d.ts +0 -27
  255. package/dist/utils/examples.d.ts.map +0 -1
  256. package/dist/utils/examples.js +0 -438
  257. package/dist/utils/examples.js.map +0 -1
  258. package/dist/utils/scaffold.d.ts +0 -59
  259. package/dist/utils/scaffold.d.ts.map +0 -1
  260. package/dist/utils/scaffold.js +0 -637
  261. package/dist/utils/scaffold.js.map +0 -1
@@ -0,0 +1,77 @@
1
+ export const utilityPatterns = {
2
+ 'zod-schema': {
3
+ file: 'src/lib/validations.ts',
4
+ description: 'Zod validation schemas with type inference',
5
+ keyPoints: [
6
+ 'z.object() for form schemas',
7
+ 'Chained validations (min, max, email, regex)',
8
+ 'Custom error messages',
9
+ 'Type inference with z.infer<>',
10
+ 'refine() for cross-field validation (password confirmation)',
11
+ ],
12
+ },
13
+ 'form-error-component': {
14
+ file: 'src/components/ui/form-error.tsx',
15
+ description: 'Form error display components',
16
+ keyPoints: [
17
+ 'FieldErrorMessage for single field',
18
+ 'FormErrorSummary for all errors',
19
+ 'RootFormError for form-level errors',
20
+ 'Integrates with react-hook-form types',
21
+ ],
22
+ },
23
+ 'register-form': {
24
+ file: 'src/components/shared/RegisterForm/RegisterForm.tsx',
25
+ description: 'Complete form component with validation',
26
+ keyPoints: [
27
+ 'Uses useRegisterForm custom hook',
28
+ 'Inline error display with FieldErrorMessage',
29
+ 'form.register() for input binding',
30
+ 'Disabled submit during isSubmitting',
31
+ 'Lingui Trans for i18n labels',
32
+ 'Demonstrates cross-field validation UX',
33
+ ],
34
+ },
35
+ 'theme-toggle': {
36
+ file: 'src/components/shared/ThemeToggle/ThemeToggle.tsx',
37
+ description: 'Theme toggle with system preference',
38
+ keyPoints: [
39
+ 'Three modes: light, dark, system',
40
+ 'Zustand store for persistence',
41
+ 'getResolvedTheme() for actual value',
42
+ 'Icon changes based on resolved theme',
43
+ ],
44
+ },
45
+ 'main-entry': {
46
+ file: 'src/main.tsx',
47
+ description: 'Application entry point with Sentry initialization',
48
+ keyPoints: [
49
+ 'Lazy Sentry initialization with requestIdleCallback for web vitals',
50
+ 'Global error handlers (window.onerror, onunhandledrejection)',
51
+ 'i18n initialization before render',
52
+ 'Provider hierarchy: Query → I18n → Router → Mobile → ErrorBoundary',
53
+ 'Multi-tab preferences sync with HMR cleanup',
54
+ ],
55
+ },
56
+ 'lib-config': {
57
+ file: 'src/lib/config.ts',
58
+ description: 'Centralized application configuration',
59
+ keyPoints: [
60
+ 'APP_CONFIG for app name and URL',
61
+ 'SENTRY_CONFIG with enabled flag, DSN, and tracesSampleRate',
62
+ 'Environment variables with fallback defaults',
63
+ 'as const for type inference',
64
+ ],
65
+ },
66
+ 'profiler-wrapper': {
67
+ file: 'src/contexts/performanceContext.tsx',
68
+ description: 'React Profiler wrapper component',
69
+ keyPoints: ['Wraps children with React.Profiler', 'Captures render timing', 'Only active in development'],
70
+ },
71
+ 'performance-e2e': {
72
+ file: 'e2e/performance/setup.ts',
73
+ description: 'Performance E2E test setup',
74
+ keyPoints: ['Lighthouse integration', 'Chrome launcher setup', 'Web Vitals assertions'],
75
+ },
76
+ };
77
+ //# sourceMappingURL=utility-patterns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utility-patterns.js","sourceRoot":"","sources":["../../../src/utils/examples/utility-patterns.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,eAAe,GAAe;IACzC,YAAY,EAAE;QACZ,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EAAE,4CAA4C;QACzD,SAAS,EAAE;YACT,6BAA6B;YAC7B,8CAA8C;YAC9C,uBAAuB;YACvB,+BAA+B;YAC/B,6DAA6D;SAC9D;KACF;IACD,sBAAsB,EAAE;QACtB,IAAI,EAAE,kCAAkC;QACxC,WAAW,EAAE,+BAA+B;QAC5C,SAAS,EAAE;YACT,oCAAoC;YACpC,iCAAiC;YACjC,qCAAqC;YACrC,uCAAuC;SACxC;KACF;IACD,eAAe,EAAE;QACf,IAAI,EAAE,qDAAqD;QAC3D,WAAW,EAAE,yCAAyC;QACtD,SAAS,EAAE;YACT,kCAAkC;YAClC,6CAA6C;YAC7C,mCAAmC;YACnC,qCAAqC;YACrC,8BAA8B;YAC9B,wCAAwC;SACzC;KACF;IACD,cAAc,EAAE;QACd,IAAI,EAAE,mDAAmD;QACzD,WAAW,EAAE,qCAAqC;QAClD,SAAS,EAAE;YACT,kCAAkC;YAClC,+BAA+B;YAC/B,qCAAqC;YACrC,sCAAsC;SACvC;KACF;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,cAAc;QACpB,WAAW,EAAE,oDAAoD;QACjE,SAAS,EAAE;YACT,oEAAoE;YACpE,8DAA8D;YAC9D,mCAAmC;YACnC,oEAAoE;YACpE,6CAA6C;SAC9C;KACF;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,mBAAmB;QACzB,WAAW,EAAE,uCAAuC;QACpD,SAAS,EAAE;YACT,iCAAiC;YACjC,4DAA4D;YAC5D,8CAA8C;YAC9C,6BAA6B;SAC9B;KACF;IACD,kBAAkB,EAAE;QAClB,IAAI,EAAE,qCAAqC;QAC3C,WAAW,EAAE,kCAAkC;QAC/C,SAAS,EAAE,CAAC,oCAAoC,EAAE,wBAAwB,EAAE,4BAA4B,CAAC;KAC1G;IACD,iBAAiB,EAAE;QACjB,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,4BAA4B;QACzC,SAAS,EAAE,CAAC,wBAAwB,EAAE,uBAAuB,EAAE,uBAAuB,CAAC;KACxF;CACF,CAAC"}
@@ -1,5 +1,7 @@
1
- export { isNpxMode, TEMPLATES_ROOT, resolveTemplatePath } from './paths.js';
2
- export { resolveFeatureDependencies, mergeDependencies, mergeScripts, computeFileStructure, getConfigFiles, getSetupCommands, computeScaffold, } from './scaffold.js';
3
- export { getAvailablePatterns, getCodeExample, getFeatureExamples, type CodeExample } from './examples.js';
1
+ export { isPublishedMode, CONTENT_ROOT, resolveTemplatePath } from './paths.js';
2
+ export { createCache, createSingletonCache } from './cache.js';
3
+ export { readWithFallback, getErrorMessage } from './errors.js';
4
+ export { resolveFeatureDependencies, mergeDependencies, mergeScripts, computeFileStructure, getConfigFiles, getSetupCommands, computeScaffold, } from './scaffold/index.js';
5
+ export { getAvailablePatterns, getCodeExample, getFeatureExamples, type CodeExample } from './examples/index.js';
4
6
  export { computeDocsForFeatures, computeDocsContent } from './docs.js';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE5E,OAAO,EACL,0BAA0B,EAC1B,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,eAAe,GAChB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAE3G,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EACL,0BAA0B,EAC1B,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEjH,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC"}
@@ -1,5 +1,7 @@
1
- export { isNpxMode, TEMPLATES_ROOT, resolveTemplatePath } from './paths.js';
2
- export { resolveFeatureDependencies, mergeDependencies, mergeScripts, computeFileStructure, getConfigFiles, getSetupCommands, computeScaffold, } from './scaffold.js';
3
- export { getAvailablePatterns, getCodeExample, getFeatureExamples } from './examples.js';
1
+ export { isPublishedMode, CONTENT_ROOT, resolveTemplatePath } from './paths.js';
2
+ export { createCache, createSingletonCache } from './cache.js';
3
+ export { readWithFallback, getErrorMessage } from './errors.js';
4
+ export { resolveFeatureDependencies, mergeDependencies, mergeScripts, computeFileStructure, getConfigFiles, getSetupCommands, computeScaffold, } from './scaffold/index.js';
5
+ export { getAvailablePatterns, getCodeExample, getFeatureExamples } from './examples/index.js';
4
6
  export { computeDocsForFeatures, computeDocsContent } from './docs.js';
5
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE5E,OAAO,EACL,0BAA0B,EAC1B,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,eAAe,GAChB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,kBAAkB,EAAoB,MAAM,eAAe,CAAC;AAE3G,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEhE,OAAO,EACL,0BAA0B,EAC1B,iBAAiB,EACjB,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACd,gBAAgB,EAChB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,kBAAkB,EAAoB,MAAM,qBAAqB,CAAC;AAEjH,OAAO,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC"}
@@ -1,29 +1,19 @@
1
1
  /**
2
- * Centralized path resolution for MCP server
3
- *
4
- * Handles detection of development vs published (npx) mode.
5
- * Uses a marker file (.bundled) to reliably detect npx mode,
6
- * avoiding issues where running `npm run bundle` during development
7
- * would cause the server to read from stale bundled files.
8
- */
9
- /**
10
- * Check if running in npx/published mode
11
- *
12
- * We check for a .bundled marker file instead of just checking if
13
- * templates/ exists. This prevents issues where a developer runs
14
- * `npm run bundle` during development and then gets stale files.
2
+ * Path resolution for MCP server - handles dev vs published mode.
15
3
  */
16
- export declare const isNpxMode: boolean;
4
+ /** True when running as published npm package (npx mode). */
5
+ export declare const isPublishedMode: boolean;
6
+ /** Root directory for reading content files. */
7
+ export declare const CONTENT_ROOT: string;
8
+ /** Resolves path relative to content root, handling dotfile renames in published mode. */
9
+ export declare function resolveTemplatePath(relativePath: string): string;
17
10
  /**
18
- * Root directory for reading template files
11
+ * Check if resolved path is within CONTENT_ROOT (prevent path traversal).
19
12
  *
20
- * - In development: reads from monorepo root (live files)
21
- * - In npx mode: reads from bundled templates directory
22
- */
23
- export declare const TEMPLATES_ROOT: string;
24
- /**
25
- * Resolve a path relative to templates root.
26
- * In npx mode, handles renamed dotfiles (npm strips .gitignore during publish).
13
+ * @example
14
+ * isPathWithinRoot('vite.config.ts') // true
15
+ * isPathWithinRoot('../../../etc/passwd') // false
16
+ * isPathWithinRoot('/etc/passwd') // false
27
17
  */
28
- export declare function resolveTemplatePath(relativePath: string): string;
18
+ export declare function isPathWithinRoot(relativePath: string): boolean;
29
19
  //# sourceMappingURL=paths.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAiBH;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,SAA4B,CAAC;AAEnD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QAAgD,CAAC;AAU5E;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAShE"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA;;GAEG;AAWH,6DAA6D;AAC7D,eAAO,MAAM,eAAe,SAA4B,CAAC;AAEzD,gDAAgD;AAChD,eAAO,MAAM,YAAY,QAAiD,CAAC;AAO3E,0FAA0F;AAC1F,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAKhE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAW9D"}
@@ -1,53 +1,42 @@
1
1
  /**
2
- * Centralized path resolution for MCP server
3
- *
4
- * Handles detection of development vs published (npx) mode.
5
- * Uses a marker file (.bundled) to reliably detect npx mode,
6
- * avoiding issues where running `npm run bundle` during development
7
- * would cause the server to read from stale bundled files.
2
+ * Path resolution for MCP server - handles dev vs published mode.
8
3
  */
9
4
  import { existsSync } from 'fs';
10
- import { dirname, join } from 'path';
5
+ import { dirname, isAbsolute, join, normalize } from 'path';
11
6
  import { fileURLToPath } from 'url';
12
7
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
- // Path to bundled templates (for npx distribution)
14
- const BUNDLED_TEMPLATES = join(__dirname, '..', '..', 'templates');
15
- // Marker file created by bundle script to indicate npx mode
16
- const BUNDLE_MARKER = join(BUNDLED_TEMPLATES, '.bundled');
17
- // Monorepo root (react-spa-scaffold app lives at root alongside packages/)
8
+ const BUNDLED_PATH = join(__dirname, '..', '..', 'templates');
9
+ const BUNDLE_MARKER = join(BUNDLED_PATH, '.bundled');
18
10
  const MONOREPO_ROOT = join(__dirname, '..', '..', '..', '..');
19
- /**
20
- * Check if running in npx/published mode
21
- *
22
- * We check for a .bundled marker file instead of just checking if
23
- * templates/ exists. This prevents issues where a developer runs
24
- * `npm run bundle` during development and then gets stale files.
25
- */
26
- export const isNpxMode = existsSync(BUNDLE_MARKER);
27
- /**
28
- * Root directory for reading template files
29
- *
30
- * - In development: reads from monorepo root (live files)
31
- * - In npx mode: reads from bundled templates directory
32
- */
33
- export const TEMPLATES_ROOT = isNpxMode ? BUNDLED_TEMPLATES : MONOREPO_ROOT;
34
- /**
35
- * Dotfiles that npm strips during publish, renamed in bundled templates.
36
- * Maps from original name → bundled name
37
- */
11
+ /** True when running as published npm package (npx mode). */
12
+ export const isPublishedMode = existsSync(BUNDLE_MARKER);
13
+ /** Root directory for reading content files. */
14
+ export const CONTENT_ROOT = isPublishedMode ? BUNDLED_PATH : MONOREPO_ROOT;
15
+ /** Dotfiles renamed in bundled templates (npm strips .gitignore). */
38
16
  const RENAMED_DOTFILES = {
39
17
  '.gitignore': 'gitignore',
40
18
  };
19
+ /** Resolves path relative to content root, handling dotfile renames in published mode. */
20
+ export function resolveTemplatePath(relativePath) {
21
+ const resolvedPath = isPublishedMode && RENAMED_DOTFILES[relativePath] ? RENAMED_DOTFILES[relativePath] : relativePath;
22
+ return join(CONTENT_ROOT, resolvedPath);
23
+ }
41
24
  /**
42
- * Resolve a path relative to templates root.
43
- * In npx mode, handles renamed dotfiles (npm strips .gitignore during publish).
25
+ * Check if resolved path is within CONTENT_ROOT (prevent path traversal).
26
+ *
27
+ * @example
28
+ * isPathWithinRoot('vite.config.ts') // true
29
+ * isPathWithinRoot('../../../etc/passwd') // false
30
+ * isPathWithinRoot('/etc/passwd') // false
44
31
  */
45
- export function resolveTemplatePath(relativePath) {
46
- let resolvedPath = relativePath;
47
- // In npx mode, use renamed versions of dotfiles that npm would strip
48
- if (isNpxMode && RENAMED_DOTFILES[relativePath]) {
49
- resolvedPath = RENAMED_DOTFILES[relativePath];
32
+ export function isPathWithinRoot(relativePath) {
33
+ // Reject absolute paths - they bypass CONTENT_ROOT
34
+ if (isAbsolute(relativePath)) {
35
+ return false;
50
36
  }
51
- return join(TEMPLATES_ROOT, resolvedPath);
37
+ const fullPath = join(CONTENT_ROOT, relativePath);
38
+ const normalizedPath = normalize(fullPath);
39
+ const normalizedRoot = normalize(CONTENT_ROOT);
40
+ return normalizedPath.startsWith(normalizedRoot + '/') || normalizedPath === normalizedRoot;
52
41
  }
53
42
  //# sourceMappingURL=paths.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,mDAAmD;AACnD,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAEnE,4DAA4D;AAC5D,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;AAE1D,2EAA2E;AAC3E,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,aAAa,CAAC;AAE5E;;;GAGG;AACH,MAAM,gBAAgB,GAA2B;IAC/C,YAAY,EAAE,WAAW;CAC1B,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,IAAI,YAAY,GAAG,YAAY,CAAC;IAEhC,qEAAqE;IACrE,IAAI,SAAS,IAAI,gBAAgB,CAAC,YAAY,CAAC,EAAE,CAAC;QAChD,YAAY,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;AAC9D,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;AACrD,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAE9D,6DAA6D;AAC7D,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;AAEzD,gDAAgD;AAChD,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC;AAE3E,qEAAqE;AACrE,MAAM,gBAAgB,GAA2B;IAC/C,YAAY,EAAE,WAAW;CAC1B,CAAC;AAEF,0FAA0F;AAC1F,MAAM,UAAU,mBAAmB,CAAC,YAAoB;IACtD,MAAM,YAAY,GAChB,eAAe,IAAI,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;IAEpG,OAAO,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;AAC1C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,YAAoB;IACnD,mDAAmD;IACnD,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAClD,MAAM,cAAc,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,cAAc,GAAG,SAAS,CAAC,YAAY,CAAC,CAAC;IAE/C,OAAO,cAAc,CAAC,UAAU,CAAC,cAAc,GAAG,GAAG,CAAC,IAAI,cAAc,KAAK,cAAc,CAAC;AAC9F,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=paths.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.test.d.ts","sourceRoot":"","sources":["../../src/utils/paths.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,38 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { isPublishedMode, CONTENT_ROOT, resolveTemplatePath, isPathWithinRoot } from './paths.js';
3
+ describe('paths', () => {
4
+ it('isPublishedMode is boolean', () => {
5
+ expect(typeof isPublishedMode).toBe('boolean');
6
+ });
7
+ it('CONTENT_ROOT is non-empty string', () => {
8
+ expect(typeof CONTENT_ROOT).toBe('string');
9
+ expect(CONTENT_ROOT.length).toBeGreaterThan(0);
10
+ });
11
+ it('resolveTemplatePath returns full path', () => {
12
+ const result = resolveTemplatePath('package.json');
13
+ expect(result).toContain('package.json');
14
+ expect(result).toContain(CONTENT_ROOT);
15
+ });
16
+ });
17
+ describe('isPathWithinRoot', () => {
18
+ it('returns true for valid paths', () => {
19
+ expect(isPathWithinRoot('vite.config.ts')).toBe(true);
20
+ expect(isPathWithinRoot('docs/ARCHITECTURE.md')).toBe(true);
21
+ expect(isPathWithinRoot('src/main.tsx')).toBe(true);
22
+ expect(isPathWithinRoot('.gitignore')).toBe(true);
23
+ });
24
+ it('returns false for path traversal attempts', () => {
25
+ expect(isPathWithinRoot('../../../etc/passwd')).toBe(false);
26
+ expect(isPathWithinRoot('../../secret')).toBe(false);
27
+ expect(isPathWithinRoot('/etc/passwd')).toBe(false);
28
+ });
29
+ it('returns false for paths with embedded traversal', () => {
30
+ expect(isPathWithinRoot('docs/../../../etc/passwd')).toBe(false);
31
+ expect(isPathWithinRoot('src/../../outside')).toBe(false);
32
+ });
33
+ it('returns true for paths with internal traversal that stay within root', () => {
34
+ expect(isPathWithinRoot('src/../package.json')).toBe(true);
35
+ expect(isPathWithinRoot('docs/../vite.config.ts')).toBe(true);
36
+ });
37
+ });
38
+ //# sourceMappingURL=paths.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.test.js","sourceRoot":"","sources":["../../src/utils/paths.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAElG,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,OAAO,YAAY,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,MAAM,GAAG,mBAAmB,CAAC,cAAc,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,CAAC,gBAAgB,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpD,MAAM,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5D,MAAM,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,MAAM,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,gBAAgB,CAAC,0BAA0B,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjE,MAAM,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,MAAM,CAAC,gBAAgB,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,CAAC,gBAAgB,CAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * CLAUDE.md generator - composes sections based on selected features.
3
+ */
4
+ import type { FeatureId } from '../../../features/types.js';
5
+ /** Generates CLAUDE.md content based on selected features. */
6
+ export declare function generateClaudeMd(featureIds: FeatureId[], projectName: string, scripts: Record<string, string>): string;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/utils/scaffold/claude-md/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAe5D,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,SAAS,EAAE,EACvB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAgBR"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * CLAUDE.md generator - composes sections based on selected features.
3
+ */
4
+ import { FEATURE } from '../../../constants.js';
5
+ import { generateHeader, generateCommandsSection, generateStructureSection, generateCodePatternsSection, generateUiSection, generateMobileSection, generateThemingSection, generateMcpSection, generateI18nSection, generateTestingSection, generateGotchasSection, } from './sections.js';
6
+ /** Generates CLAUDE.md content based on selected features. */
7
+ export function generateClaudeMd(featureIds, projectName, scripts) {
8
+ const sections = [
9
+ generateHeader(projectName),
10
+ generateCommandsSection(scripts),
11
+ generateStructureSection(featureIds),
12
+ generateCodePatternsSection(featureIds),
13
+ featureIds.includes(FEATURE.UI) && generateUiSection(),
14
+ featureIds.includes(FEATURE.MOBILE) && generateMobileSection(),
15
+ featureIds.includes(FEATURE.THEMING) && generateThemingSection(),
16
+ generateMcpSection(featureIds),
17
+ featureIds.includes(FEATURE.I18N) && generateI18nSection(),
18
+ featureIds.includes(FEATURE.TESTING) && generateTestingSection(),
19
+ generateGotchasSection(featureIds),
20
+ ];
21
+ return sections.filter(Boolean).join('\n');
22
+ }
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/utils/scaffold/claude-md/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,wBAAwB,EACxB,2BAA2B,EAC3B,iBAAiB,EACjB,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,8DAA8D;AAC9D,MAAM,UAAU,gBAAgB,CAC9B,UAAuB,EACvB,WAAmB,EACnB,OAA+B;IAE/B,MAAM,QAAQ,GAAG;QACf,cAAc,CAAC,WAAW,CAAC;QAC3B,uBAAuB,CAAC,OAAO,CAAC;QAChC,wBAAwB,CAAC,UAAU,CAAC;QACpC,2BAA2B,CAAC,UAAU,CAAC;QACvC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,iBAAiB,EAAE;QACtD,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,qBAAqB,EAAE;QAC9D,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,sBAAsB,EAAE;QAChE,kBAAkB,CAAC,UAAU,CAAC;QAC9B,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,mBAAmB,EAAE;QAC1D,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,sBAAsB,EAAE;QAChE,sBAAsB,CAAC,UAAU,CAAC;KACnC,CAAC;IAEF,OAAO,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * CLAUDE.md section generators - each function generates one section.
3
+ */
4
+ import type { FeatureId } from '../../../features/types.js';
5
+ export declare function generateHeader(projectName: string): string;
6
+ export declare function generateCommandsSection(scripts: Record<string, string>): string;
7
+ export declare function generateStructureSection(featureIds: FeatureId[]): string;
8
+ export declare function generateCodePatternsSection(featureIds: FeatureId[]): string;
9
+ export declare function generateUiSection(): string;
10
+ export declare function generateMobileSection(): string;
11
+ export declare function generateThemingSection(): string;
12
+ export declare function generateMcpSection(featureIds: FeatureId[]): string;
13
+ export declare function generateI18nSection(): string;
14
+ export declare function generateTestingSection(): string;
15
+ export declare function generateGotchasSection(featureIds: FeatureId[]): string;
16
+ //# sourceMappingURL=sections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sections.d.ts","sourceRoot":"","sources":["../../../../src/utils/scaffold/claude-md/sections.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAqB5D,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAe/E;AAED,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAuCxE;AAED,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAkB3E;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAmB1C;AAED,wBAAgB,qBAAqB,IAAI,MAAM,CAoC9C;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAuB/C;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAwClE;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAY5C;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAe/C;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAoBtE"}
@@ -0,0 +1,269 @@
1
+ /**
2
+ * CLAUDE.md section generators - each function generates one section.
3
+ */
4
+ import { FEATURE } from '../../../constants.js';
5
+ /** Script descriptions for commands section. */
6
+ const SCRIPT_DESCRIPTIONS = {
7
+ dev: 'Dev server at localhost:5173',
8
+ build: 'Production build (typecheck + bundle)',
9
+ preview: 'Preview production build',
10
+ typecheck: 'TypeScript only',
11
+ lint: 'ESLint check',
12
+ 'lint:fix': 'ESLint auto-fix',
13
+ format: 'Prettier format',
14
+ 'format:check': 'Prettier check',
15
+ test: 'Vitest once',
16
+ 'test:watch': 'Vitest watch mode',
17
+ 'test:coverage': 'Coverage (80% threshold)',
18
+ e2e: 'Playwright E2E',
19
+ 'e2e:ui': 'Playwright UI mode',
20
+ 'i18n:extract': 'Extract translations to .po',
21
+ prepare: 'Initialize Husky hooks',
22
+ };
23
+ export function generateHeader(projectName) {
24
+ return `# CLAUDE.md
25
+
26
+ AI assistant guidance for **${projectName}** - a React 19 + TypeScript + Vite 7 codebase.`;
27
+ }
28
+ export function generateCommandsSection(scripts) {
29
+ const commandLines = Object.keys(scripts)
30
+ .sort()
31
+ .map((script) => {
32
+ const desc = SCRIPT_DESCRIPTIONS[script] || '';
33
+ const padding = ' '.repeat(Math.max(1, 20 - script.length));
34
+ return `npm run ${script}${padding}# ${desc}`;
35
+ });
36
+ return `
37
+ ## Commands
38
+
39
+ \`\`\`bash
40
+ ${commandLines.join('\n')}
41
+ \`\`\``;
42
+ }
43
+ export function generateStructureSection(featureIds) {
44
+ const parts = ['src/', '├── components/ # ui/ (primitives), layout/, shared/ (features)'];
45
+ if (featureIds.includes(FEATURE.API) || featureIds.includes(FEATURE.I18N) || featureIds.includes(FEATURE.MOBILE)) {
46
+ parts.push('├── contexts/ # React Context providers');
47
+ }
48
+ parts.push('├── hooks/ # Custom hooks');
49
+ const libParts = ['config', 'utils', 'format'];
50
+ if (featureIds.includes(FEATURE.API))
51
+ libParts.push('api');
52
+ if (featureIds.includes(FEATURE.ROUTING))
53
+ libParts.push('routes');
54
+ if (featureIds.includes(FEATURE.STATE))
55
+ libParts.push('storage');
56
+ parts.push(`├── lib/ # ${libParts.join(', ')}`);
57
+ if (featureIds.includes(FEATURE.ROUTING))
58
+ parts.push('├── pages/ # Lazy-loaded route components');
59
+ if (featureIds.includes(FEATURE.STATE))
60
+ parts.push('├── stores/ # Zustand stores');
61
+ if (featureIds.includes(FEATURE.I18N)) {
62
+ parts.push('├── i18n/ # LinguiJS config and catalogs');
63
+ parts.push('├── locales/ # Translation files (.po)');
64
+ }
65
+ parts.push('└── types/ # TypeScript definitions');
66
+ if (featureIds.includes(FEATURE.TESTING)) {
67
+ parts.push('', '# Unit tests co-located: *.test.ts/tsx next to source');
68
+ parts.push('e2e/tests/ # Playwright functional E2E tests');
69
+ if (featureIds.includes(FEATURE.PERFORMANCE)) {
70
+ parts.push('e2e/performance/ # Performance regression tests');
71
+ }
72
+ }
73
+ return `
74
+ ## Project Structure
75
+
76
+ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for full structure and data flow.
77
+
78
+ \`\`\`
79
+ ${parts.join('\n')}
80
+ \`\`\``;
81
+ }
82
+ export function generateCodePatternsSection(featureIds) {
83
+ const stateHierarchy = [];
84
+ if (featureIds.includes(FEATURE.STATE))
85
+ stateHierarchy.push('Zustand (persisted)');
86
+ if (featureIds.includes(FEATURE.API))
87
+ stateHierarchy.push('TanStack Query (server)');
88
+ stateHierarchy.push('Context (UI)', 'useState (local)');
89
+ return `
90
+ ## Code Patterns
91
+
92
+ **Imports**: Always use \`@/\` path alias
93
+
94
+ **Components**: Named exports + \`Props\` interface. Pages use default exports for lazy loading.
95
+
96
+ **TypeScript**: \`type\` for unions, \`interface\` for objects
97
+
98
+ **State hierarchy**: ${stateHierarchy.join(' → ')}
99
+
100
+ See [docs/CODING_STANDARDS.md](docs/CODING_STANDARDS.md) and [docs/COMPONENT_GUIDELINES.md](docs/COMPONENT_GUIDELINES.md).`;
101
+ }
102
+ export function generateUiSection() {
103
+ return `
104
+ ## UI Components (Shadcn/UI)
105
+
106
+ This project uses **Shadcn/UI** with radix-nova style. Components live in \`src/components/ui/\`.
107
+
108
+ ### Adding New Components
109
+
110
+ \`\`\`bash
111
+ npx shadcn@latest add button # Single component
112
+ npx shadcn@latest add dialog card input # Multiple components
113
+ \`\`\`
114
+
115
+ **Pattern**: Import directly (no barrel exports for UI):
116
+
117
+ \`\`\`tsx
118
+ import { Button } from '@/components/ui/button';
119
+ import { cn } from '@/lib/utils';
120
+ \`\`\``;
121
+ }
122
+ export function generateMobileSection() {
123
+ return `
124
+ ## Mobile & Responsive Design
125
+
126
+ This project includes mobile-first responsive utilities.
127
+
128
+ ### Viewport Detection
129
+
130
+ \`\`\`tsx
131
+ import { MobileProvider, useMobileContext } from '@/contexts/mobileContext';
132
+
133
+ // Wrap app with MobileProvider
134
+ <MobileProvider>{children}</MobileProvider>
135
+
136
+ // Use in components
137
+ const { isMobile, isTablet, isDesktop, width } = useMobileContext();
138
+ \`\`\`
139
+
140
+ ### Breakpoints
141
+
142
+ \`\`\`tsx
143
+ import { BREAKPOINTS, useIsMobile, useIsDesktop } from '@/hooks/useMediaQuery';
144
+
145
+ // BREAKPOINTS: sm (640), md (768), lg (1024), xl (1280)
146
+ const isMobile = useIsMobile(); // width < 768px
147
+ const isDesktop = useIsDesktop(); // width >= 1024px
148
+ \`\`\`
149
+
150
+ ### Touch-Aware Sizing
151
+
152
+ \`\`\`tsx
153
+ import { useTouchSizes } from '@/hooks/useTouchSizes';
154
+
155
+ const sizes = useTouchSizes();
156
+ <Button size={sizes.button}>Click</Button> // 'touch' on mobile, 'default' on desktop
157
+ \`\`\``;
158
+ }
159
+ export function generateThemingSection() {
160
+ return `
161
+ ## Theming
162
+
163
+ Light/dark/system theme support with Zustand persistence.
164
+
165
+ ### Usage
166
+
167
+ \`\`\`tsx
168
+ import { usePreferencesStore } from '@/stores/preferencesStore';
169
+
170
+ // Get current theme
171
+ const theme = usePreferencesStore((s) => s.theme);
172
+
173
+ // Toggle theme
174
+ const toggleTheme = usePreferencesStore((s) => s.toggleTheme);
175
+
176
+ // Get resolved theme (actual light/dark value when 'system')
177
+ const getResolvedTheme = usePreferencesStore((s) => s.getResolvedTheme);
178
+ \`\`\`
179
+
180
+ The \`useThemeEffect\` hook automatically applies the \`.dark\` class to the document.
181
+ The ThemeToggle component provides a UI for switching between light, dark, and system themes.`;
182
+ }
183
+ export function generateMcpSection(featureIds) {
184
+ let section = `
185
+ ## MCP Servers (PREFER OVER WebSearch)
186
+
187
+ Use MCP servers for documentation lookup. They provide **structured, version-accurate data** directly from source.`;
188
+ if (featureIds.includes(FEATURE.UI)) {
189
+ section += `
190
+
191
+ ### Shadcn MCP (UI Components)
192
+
193
+ | Need | Tool |
194
+ | ------------------- | ------------------------------------------------ |
195
+ | Find component | \`mcp__shadcn__search_items_in_registries\` |
196
+ | View component code | \`mcp__shadcn__view_items_in_registries\` |
197
+ | Usage examples | \`mcp__shadcn__get_item_examples_from_registries\` |
198
+ | CLI add command | \`mcp__shadcn__get_add_command_for_items\` |`;
199
+ }
200
+ section += `
201
+
202
+ ### Context7 MCP (All Libraries)
203
+
204
+ Use for **any npm package** documentation:
205
+
206
+ \`\`\`
207
+ resolve-library-id → get-library-docs
208
+ \`\`\`
209
+
210
+ **Examples**: react-hook-form, @tanstack/react-query, zustand, zod, date-fns
211
+
212
+ ### Decision Flow
213
+
214
+ \`\`\`
215
+ Need UI component? → Shadcn MCP
216
+ Need library docs? → Context7 MCP (any npm package)
217
+ Need general info? → WebSearch (fallback only)
218
+ \`\`\``;
219
+ return section;
220
+ }
221
+ export function generateI18nSection() {
222
+ return `
223
+ ## Translations (CRITICAL)
224
+
225
+ All user-facing text MUST have translator comments. ESLint enforces this.
226
+
227
+ \`\`\`tsx
228
+ <Trans comment="Dashboard heading">Welcome back</Trans>
229
+ t({ message: 'Close', comment: 'Close button' })
230
+ \`\`\`
231
+
232
+ See [docs/INTERNATIONALIZATION.md](docs/INTERNATIONALIZATION.md).`;
233
+ }
234
+ export function generateTestingSection() {
235
+ return `
236
+ ## Testing
237
+
238
+ See [docs/TESTING.md](docs/TESTING.md) and [docs/E2E_TESTING.md](docs/E2E_TESTING.md).
239
+
240
+ Unit tests are **co-located** with source files (\`*.test.ts/tsx\`). 80% coverage required.
241
+
242
+ \`\`\`typescript
243
+ import { describe, it, expect, vi } from 'vitest';
244
+ import { screen, renderHook } from '@testing-library/react';
245
+ import { render, mockMatchMedia, server } from '@/test';
246
+ \`\`\`
247
+
248
+ MSW handlers auto-reset after each test.`;
249
+ }
250
+ export function generateGotchasSection(featureIds) {
251
+ const gotchas = [];
252
+ if (featureIds.includes(FEATURE.DEVTOOLS)) {
253
+ gotchas.push('**Node.js >= 22.0.0** required (check `.nvmrc`)');
254
+ gotchas.push('**Conventional commits** enforced by commitlint');
255
+ }
256
+ if (featureIds.includes(FEATURE.MOBILE)) {
257
+ gotchas.push('**Context hooks throw** outside provider (e.g., `useMobileContext()`)');
258
+ }
259
+ gotchas.push('**Barrel exports** in each directory via `index.ts`');
260
+ if (featureIds.includes(FEATURE.UI)) {
261
+ gotchas.push('**UI components** import directly: `@/components/ui/button` (no barrel)');
262
+ }
263
+ return `
264
+ ## Common Gotchas
265
+
266
+ ${gotchas.map((g, i) => `${i + 1}. ${g}`).join('\n')}
267
+ `;
268
+ }
269
+ //# sourceMappingURL=sections.js.map