@vibecodetown/mcp-server 2.1.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 (172) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +269 -0
  3. package/build/auth/gate.js +225 -0
  4. package/build/auth/index.js +55 -0
  5. package/build/auth/public_key.js +27 -0
  6. package/build/auth/token_cache.js +122 -0
  7. package/build/auth/token_verifier.js +103 -0
  8. package/build/bootstrap/doctor.js +115 -0
  9. package/build/bootstrap/installer.js +673 -0
  10. package/build/bootstrap/lock.js +37 -0
  11. package/build/bootstrap/platform.js +26 -0
  12. package/build/bootstrap/registry.js +37 -0
  13. package/build/cache/index.js +147 -0
  14. package/build/cli.js +101 -0
  15. package/build/contracts.js +22 -0
  16. package/build/control_plane/gate.js +161 -0
  17. package/build/control_plane/index.js +6 -0
  18. package/build/dx/activity.js +139 -0
  19. package/build/engine.js +106 -0
  20. package/build/errors.js +171 -0
  21. package/build/generated/activate_input.js +2 -0
  22. package/build/generated/activate_output.js +57 -0
  23. package/build/generated/advisory_review_input.js +2 -0
  24. package/build/generated/advisory_review_output.js +35 -0
  25. package/build/generated/auth_token_file.js +2 -0
  26. package/build/generated/briefing_input.js +2 -0
  27. package/build/generated/briefing_output.js +2 -0
  28. package/build/generated/clinic_bridge_file.js +13 -0
  29. package/build/generated/contracts_bundle_info.js +5 -0
  30. package/build/generated/create_work_order_input.js +2 -0
  31. package/build/generated/create_work_order_output.js +2 -0
  32. package/build/generated/current_work_order_file.js +2 -0
  33. package/build/generated/doctor_input.js +2 -0
  34. package/build/generated/doctor_output.js +24 -0
  35. package/build/generated/execution_result.js +2 -0
  36. package/build/generated/execution_task.js +2 -0
  37. package/build/generated/export_output_input.js +2 -0
  38. package/build/generated/export_output_output.js +2 -0
  39. package/build/generated/finalize_work_input.js +2 -0
  40. package/build/generated/finalize_work_output.js +2 -0
  41. package/build/generated/gate_input.js +2 -0
  42. package/build/generated/gate_output.js +2 -0
  43. package/build/generated/gate_result_v1.js +2 -0
  44. package/build/generated/get_decision_input.js +2 -0
  45. package/build/generated/get_decision_output.js +13 -0
  46. package/build/generated/handoff_to_clinic.js +2 -0
  47. package/build/generated/index.js +75 -0
  48. package/build/generated/inspect_code_input.js +2 -0
  49. package/build/generated/inspect_code_output.js +13 -0
  50. package/build/generated/memory_retrieve_output.js +2 -0
  51. package/build/generated/memory_state_file.js +2 -0
  52. package/build/generated/memory_status_input.js +2 -0
  53. package/build/generated/memory_status_output.js +13 -0
  54. package/build/generated/memory_sync_input.js +2 -0
  55. package/build/generated/memory_sync_output.js +13 -0
  56. package/build/generated/plugin_result.js +2 -0
  57. package/build/generated/react_perf_check_patterns_input.js +2 -0
  58. package/build/generated/react_perf_check_patterns_output.js +2 -0
  59. package/build/generated/react_perf_generate_report_input.js +2 -0
  60. package/build/generated/react_perf_generate_report_output.js +2 -0
  61. package/build/generated/repair_plan_input.js +2 -0
  62. package/build/generated/repair_plan_output.js +2 -0
  63. package/build/generated/run_app_input.js +2 -0
  64. package/build/generated/run_app_output.js +2 -0
  65. package/build/generated/run_state_file.js +13 -0
  66. package/build/generated/scaffold_input.js +2 -0
  67. package/build/generated/scaffold_output.js +2 -0
  68. package/build/generated/search_oss_input.js +2 -0
  69. package/build/generated/search_oss_output.js +2 -0
  70. package/build/generated/selection_validation_result.js +2 -0
  71. package/build/generated/signal_agent_input.js +2 -0
  72. package/build/generated/spec_high_ask_queue_items_file.js +2 -0
  73. package/build/generated/spec_high_clinic_bridge_output.js +2 -0
  74. package/build/generated/spec_high_decision_draft_output.js +2 -0
  75. package/build/generated/spec_high_validate_output.js +2 -0
  76. package/build/generated/status_input.js +2 -0
  77. package/build/generated/status_output.js +2 -0
  78. package/build/generated/submit_decision_input.js +2 -0
  79. package/build/generated/submit_decision_output.js +2 -0
  80. package/build/generated/tool_error_output.js +2 -0
  81. package/build/generated/undo_last_task_input.js +2 -0
  82. package/build/generated/undo_last_task_output.js +2 -0
  83. package/build/generated/update_input.js +2 -0
  84. package/build/generated/update_output.js +2 -0
  85. package/build/generated/vibe_pm_inspection_result.js +2 -0
  86. package/build/generated/vibe_pm_report_markdown.js +2 -0
  87. package/build/generated/vibe_pm_verdict.js +2 -0
  88. package/build/generated/vibe_repo_config.js +2 -0
  89. package/build/generated/vibecoding_helper_answer_output.js +2 -0
  90. package/build/generated/vibecoding_helper_one_loop_selection_output.js +2 -0
  91. package/build/generated/vibecoding_helper_show_ask_queue_output.js +2 -0
  92. package/build/generated/work_order_v1.js +2 -0
  93. package/build/generated/zoekt_evidence_input.js +2 -0
  94. package/build/generated/zoekt_evidence_output.js +2 -0
  95. package/build/index.js +111 -0
  96. package/build/legacy_alias.js +65 -0
  97. package/build/local-mode/bash.js +61 -0
  98. package/build/local-mode/config.js +171 -0
  99. package/build/local-mode/git.js +33 -0
  100. package/build/local-mode/init.js +110 -0
  101. package/build/local-mode/paths.js +24 -0
  102. package/build/local-mode/templates.js +856 -0
  103. package/build/local-mode/work-order.js +41 -0
  104. package/build/resources/index.js +246 -0
  105. package/build/security/input-validator.js +119 -0
  106. package/build/security/path-policy.js +289 -0
  107. package/build/security/sandbox.js +228 -0
  108. package/build/tools/react_perf/check_patterns.js +172 -0
  109. package/build/tools/react_perf/generate_report.js +337 -0
  110. package/build/tools/react_perf/index.js +119 -0
  111. package/build/tools/react_perf/rules/advanced.js +325 -0
  112. package/build/tools/react_perf/rules/async.js +104 -0
  113. package/build/tools/react_perf/rules/bundle.js +101 -0
  114. package/build/tools/react_perf/rules/client.js +186 -0
  115. package/build/tools/react_perf/rules/index.js +74 -0
  116. package/build/tools/react_perf/rules/js.js +148 -0
  117. package/build/tools/react_perf/rules/rendering.js +166 -0
  118. package/build/tools/react_perf/rules/rerender.js +161 -0
  119. package/build/tools/react_perf/rules/server.js +141 -0
  120. package/build/tools/react_perf/types.js +127 -0
  121. package/build/tools/vibe_pm/activate.js +102 -0
  122. package/build/tools/vibe_pm/advisory_review.js +77 -0
  123. package/build/tools/vibe_pm/briefing.js +178 -0
  124. package/build/tools/vibe_pm/context.js +439 -0
  125. package/build/tools/vibe_pm/create_work_order.js +271 -0
  126. package/build/tools/vibe_pm/doc_status_gate.js +370 -0
  127. package/build/tools/vibe_pm/doctor.js +262 -0
  128. package/build/tools/vibe_pm/entity_gate/preflight.js +78 -0
  129. package/build/tools/vibe_pm/export_output.js +135 -0
  130. package/build/tools/vibe_pm/finalize_work.js +393 -0
  131. package/build/tools/vibe_pm/gate.js +33 -0
  132. package/build/tools/vibe_pm/get_decision.js +281 -0
  133. package/build/tools/vibe_pm/index.js +593 -0
  134. package/build/tools/vibe_pm/inspect_code.js +828 -0
  135. package/build/tools/vibe_pm/intent/generator.js +294 -0
  136. package/build/tools/vibe_pm/intent/index.js +5 -0
  137. package/build/tools/vibe_pm/intent/prompt_density.js +227 -0
  138. package/build/tools/vibe_pm/intent/types.js +70 -0
  139. package/build/tools/vibe_pm/intent/verifier.js +237 -0
  140. package/build/tools/vibe_pm/kce/doc_usage.js +51 -0
  141. package/build/tools/vibe_pm/kce/on_finalize.js +11 -0
  142. package/build/tools/vibe_pm/kce/preflight.js +232 -0
  143. package/build/tools/vibe_pm/local_memory.js +26 -0
  144. package/build/tools/vibe_pm/memory_status.js +82 -0
  145. package/build/tools/vibe_pm/memory_sync.js +134 -0
  146. package/build/tools/vibe_pm/modules/decision_snapshot.js +29 -0
  147. package/build/tools/vibe_pm/modules/ensure.js +100 -0
  148. package/build/tools/vibe_pm/modules/fingerprint.js +30 -0
  149. package/build/tools/vibe_pm/modules/fix_dependencies.js +394 -0
  150. package/build/tools/vibe_pm/modules/planning_v1.js +110 -0
  151. package/build/tools/vibe_pm/modules/repo_context.js +56 -0
  152. package/build/tools/vibe_pm/modules/research_v1.js +114 -0
  153. package/build/tools/vibe_pm/modules/skills_v1.js +100 -0
  154. package/build/tools/vibe_pm/pm_language.js +222 -0
  155. package/build/tools/vibe_pm/repair_plan.js +199 -0
  156. package/build/tools/vibe_pm/run_app.js +597 -0
  157. package/build/tools/vibe_pm/run_app_podman.js +64 -0
  158. package/build/tools/vibe_pm/scaffold.js +550 -0
  159. package/build/tools/vibe_pm/search_oss.js +124 -0
  160. package/build/tools/vibe_pm/status.js +153 -0
  161. package/build/tools/vibe_pm/submit_decision.js +87 -0
  162. package/build/tools/vibe_pm/system_design/issue_mapping.js +47 -0
  163. package/build/tools/vibe_pm/system_design/rulebook.js +112 -0
  164. package/build/tools/vibe_pm/system_design/semgrep.js +132 -0
  165. package/build/tools/vibe_pm/types.js +229 -0
  166. package/build/tools/vibe_pm/undo_last_task.js +163 -0
  167. package/build/tools/vibe_pm/update.js +146 -0
  168. package/build/tools/vibe_pm/zoekt_evidence.js +96 -0
  169. package/build/tools.js +269 -0
  170. package/build/version-check.js +239 -0
  171. package/build/vibe-cli.js +631 -0
  172. package/package.json +76 -0
@@ -0,0 +1,325 @@
1
+ // adapters/mcp-ts/src/tools/react_perf/rules/advanced.ts
2
+ // Advanced Next.js Patterns (Parallel Routes, PPR, Optimistic Updates)
3
+ /**
4
+ * Advanced rules detect missed opportunities for Next.js advanced features
5
+ * like parallel routes, partial prerendering, and optimistic updates.
6
+ */
7
+ export const ADVANCED_RULES = [
8
+ {
9
+ id: "advanced-parallel-routes",
10
+ category: "advanced",
11
+ impact: "LOW-MEDIUM",
12
+ // Detects: layout with multiple independent data-fetching sections
13
+ pattern: /export\s+(?:default\s+)?(?:async\s+)?function\s+Layout[\s\S]*?(?:await|fetch\()[\s\S]*?(?:await|fetch\()/,
14
+ antiPattern: /@\w+|parallel/i,
15
+ message: "Layout with multiple data sources may benefit from Parallel Routes",
16
+ suggestion: "Use @folder convention for independent loading states",
17
+ fileFilter: /layout\.(tsx?|jsx?)$/,
18
+ examples: {
19
+ bad: `// app/dashboard/layout.tsx
20
+ export default async function Layout({ children }) {
21
+ const user = await getUser();
22
+ const notifications = await getNotifications();
23
+ return (
24
+ <div>
25
+ <Sidebar user={user} />
26
+ <NotificationPanel data={notifications} />
27
+ {children}
28
+ </div>
29
+ );
30
+ }`,
31
+ good: `// app/dashboard/layout.tsx
32
+ export default function Layout({
33
+ children,
34
+ sidebar, // @sidebar/page.tsx
35
+ notifications // @notifications/page.tsx
36
+ }) {
37
+ return (
38
+ <div>
39
+ {sidebar}
40
+ {notifications}
41
+ {children}
42
+ </div>
43
+ );
44
+ }`
45
+ }
46
+ },
47
+ {
48
+ id: "advanced-intercepting-routes",
49
+ category: "advanced",
50
+ impact: "LOW-MEDIUM",
51
+ // Detects: modal/dialog pattern without intercepting routes
52
+ pattern: /<(?:Modal|Dialog|Sheet|Drawer)[^>]*>[\s\S]*?<\/(?:Modal|Dialog|Sheet|Drawer)>/,
53
+ antiPattern: /\(\.\)|\.{2,}|intercepting/i,
54
+ message: "Modal pattern may benefit from Intercepting Routes",
55
+ suggestion: "Use (.) or (..) convention for URL-backed modals",
56
+ fileFilter: /\.(tsx?|jsx?)$/,
57
+ examples: {
58
+ bad: `// Basic modal loses URL context
59
+ function PhotoGallery({ photos }) {
60
+ const [selectedPhoto, setSelectedPhoto] = useState(null);
61
+
62
+ return (
63
+ <>
64
+ {photos.map(p => (
65
+ <img onClick={() => setSelectedPhoto(p)} />
66
+ ))}
67
+ {selectedPhoto && (
68
+ <Modal onClose={() => setSelectedPhoto(null)}>
69
+ <PhotoDetail photo={selectedPhoto} />
70
+ </Modal>
71
+ )}
72
+ </>
73
+ );
74
+ }`,
75
+ good: `// app/photos/(..)photo/[id]/page.tsx (intercepts)
76
+ // Enables: shareable URL, back button works, soft navigation
77
+
78
+ // app/photos/page.tsx
79
+ function PhotoGallery({ photos }) {
80
+ return photos.map(p => (
81
+ <Link href={\`/photo/\${p.id}\`}>
82
+ <img src={p.thumbnail} />
83
+ </Link>
84
+ ));
85
+ }`
86
+ }
87
+ },
88
+ {
89
+ id: "advanced-generatestaticparams",
90
+ category: "advanced",
91
+ impact: "LOW-MEDIUM",
92
+ // Detects: dynamic route page without generateStaticParams
93
+ pattern: /export\s+(?:default\s+)?(?:async\s+)?function\s+Page\s*\(\s*\{\s*params/,
94
+ antiPattern: /generateStaticParams|dynamic\s*=\s*['"]force-dynamic['"]/,
95
+ message: "Dynamic route may benefit from generateStaticParams",
96
+ suggestion: "Add generateStaticParams for static generation of known paths",
97
+ fileFilter: /\[.*\].*page\.(tsx?|jsx?)$/,
98
+ examples: {
99
+ bad: `// app/blog/[slug]/page.tsx
100
+ export default async function Page({ params }) {
101
+ const post = await getPost(params.slug);
102
+ return <Article post={post} />;
103
+ }`,
104
+ good: `// app/blog/[slug]/page.tsx
105
+ export async function generateStaticParams() {
106
+ const posts = await getAllPosts();
107
+ return posts.map(post => ({ slug: post.slug }));
108
+ }
109
+
110
+ export default async function Page({ params }) {
111
+ const post = await getPost(params.slug);
112
+ return <Article post={post} />;
113
+ }`
114
+ }
115
+ },
116
+ {
117
+ id: "advanced-route-segment-config",
118
+ category: "advanced",
119
+ impact: "LOW-MEDIUM",
120
+ // Detects: page with specific caching needs without config
121
+ pattern: /export\s+(?:default\s+)?(?:async\s+)?function\s+Page[\s\S]*?(?:revalidate|cache|unstable_cache)/,
122
+ antiPattern: /export\s+const\s+(?:dynamic|revalidate|fetchCache|runtime)/,
123
+ message: "Page may benefit from Route Segment Config",
124
+ suggestion: "Add export const revalidate/dynamic/fetchCache for explicit caching control",
125
+ fileFilter: /page\.(tsx?|jsx?)$/,
126
+ examples: {
127
+ bad: `export default async function Page() {
128
+ const data = await fetch(url, {
129
+ next: { revalidate: 3600 }
130
+ });
131
+ }`,
132
+ good: `export const revalidate = 3600; // Applies to all fetches
133
+
134
+ export default async function Page() {
135
+ const data = await fetch(url);
136
+ }`
137
+ }
138
+ },
139
+ {
140
+ id: "advanced-metadata-missing",
141
+ category: "advanced",
142
+ impact: "MEDIUM-HIGH",
143
+ // Detects: page.tsx without metadata export
144
+ pattern: /export\s+(?:default\s+)?(?:async\s+)?function\s+(?:Page|default)/,
145
+ antiPattern: /export\s+(?:const|async\s+function)\s+(?:metadata|generateMetadata)/,
146
+ message: "Page lacks Metadata API for SEO optimization",
147
+ suggestion: "Add metadata export or generateMetadata function",
148
+ fileFilter: /page\.(tsx?|jsx?)$/,
149
+ examples: {
150
+ bad: `export default function Page() {
151
+ return <div>My Page</div>;
152
+ }`,
153
+ good: `export const metadata = {
154
+ title: 'My Page',
155
+ description: 'Page description for SEO'
156
+ };
157
+
158
+ export default function Page() {
159
+ return <div>My Page</div>;
160
+ }
161
+
162
+ // Or dynamic metadata
163
+ export async function generateMetadata({ params }) {
164
+ const post = await getPost(params.slug);
165
+ return { title: post.title };
166
+ }`
167
+ }
168
+ },
169
+ {
170
+ id: "advanced-use-optimistic",
171
+ category: "advanced",
172
+ impact: "LOW-MEDIUM",
173
+ // Detects: form with Server Action but no optimistic update
174
+ pattern: /action=\{(?:async\s*)?\([^)]*\)\s*=>|formAction|useFormState/,
175
+ antiPattern: /useOptimistic|optimistic/i,
176
+ message: "Form action may benefit from optimistic updates",
177
+ suggestion: "Use useOptimistic hook for instant UI feedback",
178
+ fileFilter: /\.(tsx?|jsx?)$/,
179
+ examples: {
180
+ bad: `function TodoList({ todos }) {
181
+ async function addTodo(formData) {
182
+ 'use server';
183
+ await db.todos.create(formData);
184
+ }
185
+
186
+ return (
187
+ <form action={addTodo}>
188
+ <input name="title" />
189
+ <button>Add</button> {/* Waits for server response */}
190
+ </form>
191
+ );
192
+ }`,
193
+ good: `function TodoList({ todos }) {
194
+ const [optimisticTodos, addOptimistic] = useOptimistic(
195
+ todos,
196
+ (state, newTodo) => [...state, { ...newTodo, pending: true }]
197
+ );
198
+
199
+ async function addTodo(formData) {
200
+ addOptimistic({ title: formData.get('title') });
201
+ await createTodoAction(formData);
202
+ }
203
+ }`
204
+ }
205
+ },
206
+ {
207
+ id: "advanced-server-actions-inline",
208
+ category: "advanced",
209
+ impact: "LOW-MEDIUM",
210
+ // Detects: multiple inline 'use server' functions
211
+ pattern: /(?:async\s+function\s+\w+[^{]*\{[^}]*['"]use server['"]\s*;[\s\S]*?){2,}/,
212
+ message: "Multiple inline Server Actions may hurt maintainability",
213
+ suggestion: "Extract Server Actions to separate actions.ts file",
214
+ fileFilter: /\.(tsx?|jsx?)$/,
215
+ examples: {
216
+ bad: `export default function Page() {
217
+ async function createPost() {
218
+ 'use server';
219
+ // action 1
220
+ }
221
+
222
+ async function deletePost() {
223
+ 'use server';
224
+ // action 2
225
+ }
226
+
227
+ async function updatePost() {
228
+ 'use server';
229
+ // action 3
230
+ }
231
+ }`,
232
+ good: `// app/posts/actions.ts
233
+ 'use server'
234
+
235
+ export async function createPost(formData) { ... }
236
+ export async function deletePost(id) { ... }
237
+ export async function updatePost(id, formData) { ... }
238
+
239
+ // app/posts/page.tsx
240
+ import { createPost, deletePost } from './actions';`
241
+ }
242
+ },
243
+ {
244
+ id: "advanced-partial-prerendering",
245
+ category: "advanced",
246
+ impact: "LOW-MEDIUM",
247
+ // Detects: page with mix of static and dynamic content
248
+ pattern: /(?:export\s+const\s+revalidate|cache:\s*['"]no-store['"])[\s\S]*?(?:<[A-Z]\w+|<div)/,
249
+ antiPattern: /Suspense|experimental_ppr/i,
250
+ message: "Page with mixed content may benefit from Partial Prerendering",
251
+ suggestion: "Enable PPR and wrap dynamic parts with Suspense",
252
+ fileFilter: /page\.(tsx?|jsx?)$/,
253
+ examples: {
254
+ bad: `// Entire page is dynamic
255
+ export const dynamic = 'force-dynamic';
256
+
257
+ export default async function Page() {
258
+ const user = await getUser(); // Dynamic
259
+ return (
260
+ <div>
261
+ <StaticHeader /> {/* Could be prerendered */}
262
+ <UserProfile user={user} />
263
+ <StaticFooter /> {/* Could be prerendered */}
264
+ </div>
265
+ );
266
+ }`,
267
+ good: `// next.config.js: experimental: { ppr: true }
268
+
269
+ export default function Page() {
270
+ return (
271
+ <div>
272
+ <StaticHeader /> {/* Prerendered */}
273
+ <Suspense fallback={<ProfileSkeleton />}>
274
+ <UserProfile /> {/* Streams in */}
275
+ </Suspense>
276
+ <StaticFooter /> {/* Prerendered */}
277
+ </div>
278
+ );
279
+ }`
280
+ }
281
+ },
282
+ {
283
+ id: "advanced-use-transition",
284
+ category: "advanced",
285
+ impact: "LOW-MEDIUM",
286
+ // Detects: heavy state update without useTransition
287
+ pattern: /set\w+\s*\(\s*(?:items|data|list|results|filtered|sorted)[\s\S]{0,50}(?:filter|map|sort)\s*\(/,
288
+ antiPattern: /useTransition|startTransition/,
289
+ message: "Heavy state update may block UI - consider useTransition",
290
+ suggestion: "Wrap non-urgent updates with startTransition for responsive UI",
291
+ fileFilter: /\.(tsx?|jsx?)$/,
292
+ examples: {
293
+ bad: `function SearchResults({ items }) {
294
+ const [query, setQuery] = useState('');
295
+ const [filteredItems, setFilteredItems] = useState(items);
296
+
297
+ function handleSearch(e) {
298
+ const q = e.target.value;
299
+ setQuery(q);
300
+ // Expensive filter blocks typing
301
+ setFilteredItems(items.filter(item =>
302
+ item.name.toLowerCase().includes(q.toLowerCase())
303
+ ));
304
+ }
305
+ }`,
306
+ good: `function SearchResults({ items }) {
307
+ const [query, setQuery] = useState('');
308
+ const [filteredItems, setFilteredItems] = useState(items);
309
+ const [isPending, startTransition] = useTransition();
310
+
311
+ function handleSearch(e) {
312
+ const q = e.target.value;
313
+ setQuery(q); // Urgent: update input immediately
314
+
315
+ startTransition(() => {
316
+ // Non-urgent: can be interrupted
317
+ setFilteredItems(items.filter(item =>
318
+ item.name.toLowerCase().includes(q.toLowerCase())
319
+ ));
320
+ });
321
+ }
322
+ }`
323
+ }
324
+ }
325
+ ];
@@ -0,0 +1,104 @@
1
+ // adapters/mcp-ts/src/tools/react_perf/rules/async.ts
2
+ // Async/Await Anti-patterns (Waterfall Detection)
3
+ /**
4
+ * Async rules detect common async/await anti-patterns that cause
5
+ * waterfall requests and unnecessary wait times.
6
+ *
7
+ * Based on Vercel agent-skills react-best-practices
8
+ */
9
+ export const ASYNC_RULES = [
10
+ {
11
+ id: "async-sequential-await",
12
+ category: "async",
13
+ impact: "CRITICAL",
14
+ // Detects: await foo(); await bar(); (sequential awaits on separate lines)
15
+ pattern: /await\s+\w+\([^)]*\)\s*;?\s*\n\s*(?:const\s+\w+\s*=\s*)?await\s+\w+\(/,
16
+ message: "Sequential await statements cause waterfall requests",
17
+ suggestion: "Use Promise.all() or Promise.allSettled() for parallel execution",
18
+ fileFilter: /\.(tsx?|jsx?)$/,
19
+ examples: {
20
+ bad: `const user = await getUser(id);
21
+ const posts = await getPosts(userId);`,
22
+ good: `const [user, posts] = await Promise.all([
23
+ getUser(id),
24
+ getPosts(userId)
25
+ ]);`
26
+ }
27
+ },
28
+ {
29
+ id: "async-await-in-loop",
30
+ category: "async",
31
+ impact: "CRITICAL",
32
+ // Detects: for/while loops with await inside
33
+ pattern: /(?:for|while)\s*\([^)]*\)\s*\{[^}]*await\s+/,
34
+ message: "await inside loop causes N sequential requests",
35
+ suggestion: "Use Promise.all() with map() for parallel execution",
36
+ fileFilter: /\.(tsx?|jsx?)$/,
37
+ examples: {
38
+ bad: `for (const id of ids) {
39
+ const data = await fetch(id);
40
+ }`,
41
+ good: `const results = await Promise.all(
42
+ ids.map(id => fetch(id))
43
+ );`
44
+ }
45
+ },
46
+ {
47
+ id: "async-defer-await",
48
+ category: "async",
49
+ impact: "HIGH",
50
+ // Detects: await before conditional that might not need the result
51
+ pattern: /const\s+\w+\s*=\s*await\s+\w+\([^)]*\)[\s\S]{0,100}if\s*\([^)]*!\s*\w+/,
52
+ message: "await executed before conditional check may be unnecessary",
53
+ suggestion: "Move await inside the branch that actually needs the data",
54
+ fileFilter: /\.(tsx?|jsx?)$/,
55
+ examples: {
56
+ bad: `const user = await getUser();
57
+ if (!needsUser) return;
58
+ // use user`,
59
+ good: `if (!needsUser) return;
60
+ const user = await getUser();
61
+ // use user`
62
+ }
63
+ },
64
+ {
65
+ id: "async-no-parallel-fetch",
66
+ category: "async",
67
+ impact: "HIGH",
68
+ // Detects: multiple fetch calls without Promise.all
69
+ pattern: /fetch\([^)]+\)[\s\S]{0,50}fetch\([^)]+\)/,
70
+ antiPattern: /Promise\.all/,
71
+ message: "Multiple fetch calls may be running sequentially",
72
+ suggestion: "Consider Promise.all() if fetches are independent",
73
+ fileFilter: /\.(tsx?|jsx?)$/,
74
+ examples: {
75
+ bad: `const res1 = await fetch('/api/users');
76
+ const res2 = await fetch('/api/posts');`,
77
+ good: `const [res1, res2] = await Promise.all([
78
+ fetch('/api/users'),
79
+ fetch('/api/posts')
80
+ ]);`
81
+ }
82
+ },
83
+ {
84
+ id: "async-use-effect-async",
85
+ category: "async",
86
+ impact: "MEDIUM-HIGH",
87
+ // Detects: async function directly in useEffect
88
+ pattern: /useEffect\s*\(\s*async\s*\(/,
89
+ message: "useEffect callback cannot be async directly",
90
+ suggestion: "Define async function inside useEffect and call it",
91
+ fileFilter: /\.(tsx?|jsx?)$/,
92
+ examples: {
93
+ bad: `useEffect(async () => {
94
+ const data = await fetchData();
95
+ }, []);`,
96
+ good: `useEffect(() => {
97
+ const fetchAndSet = async () => {
98
+ const data = await fetchData();
99
+ };
100
+ fetchAndSet();
101
+ }, []);`
102
+ }
103
+ }
104
+ ];
@@ -0,0 +1,101 @@
1
+ // adapters/mcp-ts/src/tools/react_perf/rules/bundle.ts
2
+ // Bundle Size Anti-patterns (Barrel Imports, Tree Shaking)
3
+ /**
4
+ * Bundle rules detect import patterns that increase bundle size
5
+ * and hurt initial load performance.
6
+ *
7
+ * Based on Vercel agent-skills react-best-practices
8
+ */
9
+ export const BUNDLE_RULES = [
10
+ {
11
+ id: "bundle-barrel-import",
12
+ category: "bundle",
13
+ impact: "CRITICAL",
14
+ // Detects: import { x } from '@/components' (barrel import from index)
15
+ pattern: /import\s+\{[^}]+\}\s+from\s+['"][^'"]*\/(?:components|utils|lib|hooks)['"](?!\/)$/m,
16
+ message: "Barrel import may include entire module tree in bundle",
17
+ suggestion: "Import directly from the specific file: import { X } from '@/components/X'",
18
+ fileFilter: /\.(tsx?|jsx?)$/,
19
+ examples: {
20
+ bad: `import { Button, Input } from '@/components';`,
21
+ good: `import { Button } from '@/components/Button';
22
+ import { Input } from '@/components/Input';`
23
+ }
24
+ },
25
+ {
26
+ id: "bundle-lodash-full",
27
+ category: "bundle",
28
+ impact: "CRITICAL",
29
+ // Detects: import _ from 'lodash' or import { x } from 'lodash'
30
+ pattern: /import\s+(?:_|\{[^}]+\})\s+from\s+['"]lodash['"]/,
31
+ antiPattern: /lodash\//,
32
+ message: "Full lodash import adds ~70KB to bundle",
33
+ suggestion: "Use lodash-es or import specific functions: import debounce from 'lodash/debounce'",
34
+ fileFilter: /\.(tsx?|jsx?)$/,
35
+ examples: {
36
+ bad: `import { debounce, throttle } from 'lodash';`,
37
+ good: `import debounce from 'lodash/debounce';
38
+ import throttle from 'lodash/throttle';`
39
+ }
40
+ },
41
+ {
42
+ id: "bundle-moment",
43
+ category: "bundle",
44
+ impact: "HIGH",
45
+ // Detects: import moment from 'moment'
46
+ pattern: /import\s+\w+\s+from\s+['"]moment['"]/,
47
+ message: "moment.js is 300KB+ with locales",
48
+ suggestion: "Use date-fns or dayjs instead (much smaller bundle)",
49
+ fileFilter: /\.(tsx?|jsx?)$/,
50
+ examples: {
51
+ bad: `import moment from 'moment';`,
52
+ good: `import { format, parseISO } from 'date-fns';`
53
+ }
54
+ },
55
+ {
56
+ id: "bundle-no-dynamic-import",
57
+ category: "bundle",
58
+ impact: "MEDIUM-HIGH",
59
+ // Detects: large component imports that could be dynamic
60
+ pattern: /import\s+\w+\s+from\s+['"][^'"]*(?:Modal|Dialog|Chart|Editor|Map)['"]/,
61
+ antiPattern: /dynamic|lazy/,
62
+ message: "Large component imported statically",
63
+ suggestion: "Use dynamic import for components not needed on initial render",
64
+ fileFilter: /\.(tsx?|jsx?)$/,
65
+ examples: {
66
+ bad: `import ChartComponent from './ChartComponent';`,
67
+ good: `const ChartComponent = dynamic(() => import('./ChartComponent'), {
68
+ loading: () => <Loading />
69
+ });`
70
+ }
71
+ },
72
+ {
73
+ id: "bundle-icon-library",
74
+ category: "bundle",
75
+ impact: "MEDIUM-HIGH",
76
+ // Detects: import * from icon libraries
77
+ pattern: /import\s+\*\s+as\s+\w+\s+from\s+['"](?:@heroicons|lucide-react|react-icons)['"]/,
78
+ message: "Importing entire icon library increases bundle size",
79
+ suggestion: "Import only specific icons: import { IconName } from 'library/category'",
80
+ fileFilter: /\.(tsx?|jsx?)$/,
81
+ examples: {
82
+ bad: `import * as Icons from 'lucide-react';`,
83
+ good: `import { Search, Menu } from 'lucide-react';`
84
+ }
85
+ },
86
+ {
87
+ id: "bundle-dev-dependency",
88
+ category: "bundle",
89
+ impact: "HIGH",
90
+ // Detects: imports that are typically dev-only
91
+ pattern: /import\s+.*from\s+['"](?:faker|@faker-js|msw|@testing-library)['"]/,
92
+ message: "Development dependency may be included in production bundle",
93
+ suggestion: "Ensure this import is only in test/mock files or use dynamic import with env check",
94
+ fileFilter: /\.(tsx?|jsx?)$/,
95
+ examples: {
96
+ bad: `import { faker } from '@faker-js/faker';`,
97
+ good: `// Only in *.test.ts or *.mock.ts files
98
+ import { faker } from '@faker-js/faker';`
99
+ }
100
+ }
101
+ ];