@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,337 @@
1
+ // adapters/mcp-ts/src/tools/react_perf/generate_report.ts
2
+ // Main implementation for react_perf.generate_report
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ import { impactToSeverity, calculateScore } from "./types.js";
6
+ import { checkPatterns } from "./check_patterns.js";
7
+ import { getTotalRuleCount, getRuleCountByCategory } from "./rules/index.js";
8
+ // ============================================================
9
+ // Report Generators
10
+ // ============================================================
11
+ function generateMarkdownReport(summary, violations, includeRecommendations) {
12
+ const lines = [];
13
+ // Header
14
+ lines.push("# React Performance Analysis Report");
15
+ lines.push("");
16
+ lines.push(`Generated: ${new Date().toISOString()}`);
17
+ lines.push("");
18
+ // Score Badge
19
+ const scoreEmoji = summary.score >= 80 ? "🟢" : summary.score >= 50 ? "🟡" : "🔴";
20
+ lines.push(`## Score: ${scoreEmoji} ${summary.score}/100`);
21
+ lines.push("");
22
+ // Summary Table
23
+ lines.push("## Summary");
24
+ lines.push("");
25
+ lines.push("| Metric | Value |");
26
+ lines.push("|--------|-------|");
27
+ lines.push(`| Files Scanned | ${summary.total_files_scanned} |`);
28
+ lines.push(`| Total Issues | ${summary.total_issues} |`);
29
+ lines.push(`| Errors | ${summary.by_severity.error} |`);
30
+ lines.push(`| Warnings | ${summary.by_severity.warning} |`);
31
+ lines.push(`| Info | ${summary.by_severity.info} |`);
32
+ lines.push("");
33
+ // By Category
34
+ lines.push("## Issues by Category");
35
+ lines.push("");
36
+ lines.push("| Category | Count |");
37
+ lines.push("|----------|-------|");
38
+ for (const [category, count] of Object.entries(summary.by_category)) {
39
+ if (count > 0) {
40
+ lines.push(`| ${category} | ${count} |`);
41
+ }
42
+ }
43
+ lines.push("");
44
+ // Rule Coverage
45
+ const ruleCount = getTotalRuleCount();
46
+ const categoryCount = getRuleCountByCategory();
47
+ lines.push("## Rule Coverage");
48
+ lines.push("");
49
+ lines.push(`Total rules: ${ruleCount}`);
50
+ lines.push("");
51
+ lines.push("| Category | Rules |");
52
+ lines.push("|----------|-------|");
53
+ for (const [cat, count] of Object.entries(categoryCount)) {
54
+ lines.push(`| ${cat} | ${count} |`);
55
+ }
56
+ lines.push("");
57
+ // Violations Detail
58
+ if (violations.length > 0) {
59
+ lines.push("## Violations");
60
+ lines.push("");
61
+ // Group by severity
62
+ const errors = violations.filter(v => impactToSeverity(v.impact) === "error");
63
+ const warnings = violations.filter(v => impactToSeverity(v.impact) === "warning");
64
+ const infos = violations.filter(v => impactToSeverity(v.impact) === "info");
65
+ if (errors.length > 0) {
66
+ lines.push("### 🔴 Errors");
67
+ lines.push("");
68
+ for (const v of errors) {
69
+ lines.push(`- **${v.rule_id}** in \`${v.file}:${v.line}\``);
70
+ lines.push(` - ${v.message}`);
71
+ if (includeRecommendations) {
72
+ lines.push(` - 💡 ${v.suggestion}`);
73
+ }
74
+ if (v.code_snippet) {
75
+ lines.push(` - \`${v.code_snippet}\``);
76
+ }
77
+ }
78
+ lines.push("");
79
+ }
80
+ if (warnings.length > 0) {
81
+ lines.push("### 🟡 Warnings");
82
+ lines.push("");
83
+ for (const v of warnings) {
84
+ lines.push(`- **${v.rule_id}** in \`${v.file}:${v.line}\``);
85
+ lines.push(` - ${v.message}`);
86
+ if (includeRecommendations) {
87
+ lines.push(` - 💡 ${v.suggestion}`);
88
+ }
89
+ }
90
+ lines.push("");
91
+ }
92
+ if (infos.length > 0) {
93
+ lines.push("### 🔵 Info");
94
+ lines.push("");
95
+ for (const v of infos) {
96
+ lines.push(`- **${v.rule_id}** in \`${v.file}:${v.line}\``);
97
+ lines.push(` - ${v.message}`);
98
+ if (includeRecommendations) {
99
+ lines.push(` - 💡 ${v.suggestion}`);
100
+ }
101
+ }
102
+ lines.push("");
103
+ }
104
+ }
105
+ else {
106
+ lines.push("## ✅ No violations found!");
107
+ lines.push("");
108
+ lines.push("Great job! Your code follows React performance best practices.");
109
+ lines.push("");
110
+ }
111
+ // Recommendations
112
+ if (includeRecommendations && violations.length > 0) {
113
+ lines.push("## Top 5 Recommendations");
114
+ lines.push("");
115
+ // Get unique suggestions by rule_id
116
+ const seen = new Set();
117
+ const topSuggestions = [];
118
+ for (const v of violations) {
119
+ if (!seen.has(v.rule_id) && topSuggestions.length < 5) {
120
+ seen.add(v.rule_id);
121
+ topSuggestions.push({ impact: v.impact, suggestion: v.suggestion });
122
+ }
123
+ }
124
+ for (let i = 0; i < topSuggestions.length; i++) {
125
+ const s = topSuggestions[i];
126
+ lines.push(`${i + 1}. **[${s.impact}]** ${s.suggestion}`);
127
+ }
128
+ lines.push("");
129
+ }
130
+ return lines.join("\n");
131
+ }
132
+ function generateHtmlReport(summary, violations, includeRecommendations) {
133
+ const scoreColor = summary.score >= 80 ? "#22c55e" : summary.score >= 50 ? "#eab308" : "#ef4444";
134
+ return `<!DOCTYPE html>
135
+ <html lang="en">
136
+ <head>
137
+ <meta charset="UTF-8">
138
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
139
+ <title>React Performance Report</title>
140
+ <style>
141
+ * { box-sizing: border-box; margin: 0; padding: 0; }
142
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; padding: 2rem; max-width: 1200px; margin: 0 auto; background: #f5f5f5; }
143
+ h1 { color: #1a1a1a; margin-bottom: 1rem; }
144
+ h2 { color: #333; margin: 2rem 0 1rem; border-bottom: 2px solid #ddd; padding-bottom: 0.5rem; }
145
+ h3 { color: #555; margin: 1.5rem 0 0.5rem; }
146
+ .card { background: white; border-radius: 8px; padding: 1.5rem; margin-bottom: 1rem; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
147
+ .score { font-size: 3rem; font-weight: bold; color: ${scoreColor}; }
148
+ .summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem; }
149
+ .summary-item { text-align: center; }
150
+ .summary-item .value { font-size: 2rem; font-weight: bold; }
151
+ .summary-item .label { color: #666; font-size: 0.9rem; }
152
+ table { width: 100%; border-collapse: collapse; margin: 1rem 0; }
153
+ th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #eee; }
154
+ th { background: #f9f9f9; font-weight: 600; }
155
+ .error { color: #dc2626; }
156
+ .warning { color: #ca8a04; }
157
+ .info { color: #2563eb; }
158
+ .violation { padding: 1rem; margin: 0.5rem 0; border-left: 4px solid; background: white; border-radius: 4px; }
159
+ .violation.error { border-color: #dc2626; }
160
+ .violation.warning { border-color: #ca8a04; }
161
+ .violation.info { border-color: #2563eb; }
162
+ .violation-title { font-weight: 600; }
163
+ .violation-file { color: #666; font-size: 0.9rem; font-family: monospace; }
164
+ .violation-message { margin: 0.5rem 0; }
165
+ .violation-suggestion { color: #059669; font-size: 0.9rem; }
166
+ code { background: #f3f4f6; padding: 0.2rem 0.4rem; border-radius: 4px; font-size: 0.9rem; }
167
+ .timestamp { color: #888; font-size: 0.9rem; }
168
+ </style>
169
+ </head>
170
+ <body>
171
+ <h1>🚀 React Performance Analysis Report</h1>
172
+ <p class="timestamp">Generated: ${new Date().toISOString()}</p>
173
+
174
+ <div class="card">
175
+ <h2 style="margin-top: 0;">Score</h2>
176
+ <div class="score">${summary.score}/100</div>
177
+ </div>
178
+
179
+ <div class="card">
180
+ <h2 style="margin-top: 0;">Summary</h2>
181
+ <div class="summary-grid">
182
+ <div class="summary-item">
183
+ <div class="value">${summary.total_files_scanned}</div>
184
+ <div class="label">Files Scanned</div>
185
+ </div>
186
+ <div class="summary-item">
187
+ <div class="value">${summary.total_issues}</div>
188
+ <div class="label">Total Issues</div>
189
+ </div>
190
+ <div class="summary-item">
191
+ <div class="value error">${summary.by_severity.error}</div>
192
+ <div class="label">Errors</div>
193
+ </div>
194
+ <div class="summary-item">
195
+ <div class="value warning">${summary.by_severity.warning}</div>
196
+ <div class="label">Warnings</div>
197
+ </div>
198
+ <div class="summary-item">
199
+ <div class="value info">${summary.by_severity.info}</div>
200
+ <div class="label">Info</div>
201
+ </div>
202
+ </div>
203
+ </div>
204
+
205
+ <div class="card">
206
+ <h2 style="margin-top: 0;">Issues by Category</h2>
207
+ <table>
208
+ <thead>
209
+ <tr><th>Category</th><th>Count</th></tr>
210
+ </thead>
211
+ <tbody>
212
+ ${Object.entries(summary.by_category)
213
+ .filter(([, count]) => count > 0)
214
+ .map(([cat, count]) => `<tr><td>${cat}</td><td>${count}</td></tr>`)
215
+ .join("\n ")}
216
+ </tbody>
217
+ </table>
218
+ </div>
219
+
220
+ ${violations.length > 0 ? `
221
+ <div class="card">
222
+ <h2 style="margin-top: 0;">Violations</h2>
223
+ ${violations.map(v => {
224
+ const severity = impactToSeverity(v.impact);
225
+ return `
226
+ <div class="violation ${severity}">
227
+ <div class="violation-title">${v.rule_id}</div>
228
+ <div class="violation-file">${v.file}:${v.line}</div>
229
+ <div class="violation-message">${v.message}</div>
230
+ ${includeRecommendations ? `<div class="violation-suggestion">💡 ${v.suggestion}</div>` : ""}
231
+ ${v.code_snippet ? `<div><code>${escapeHtml(v.code_snippet)}</code></div>` : ""}
232
+ </div>`;
233
+ }).join("")}
234
+ </div>
235
+ ` : `
236
+ <div class="card">
237
+ <h2 style="margin-top: 0;">✅ No violations found!</h2>
238
+ <p>Great job! Your code follows React performance best practices.</p>
239
+ </div>
240
+ `}
241
+ </body>
242
+ </html>`;
243
+ }
244
+ function escapeHtml(text) {
245
+ return text
246
+ .replace(/&/g, "&amp;")
247
+ .replace(/</g, "&lt;")
248
+ .replace(/>/g, "&gt;")
249
+ .replace(/"/g, "&quot;")
250
+ .replace(/'/g, "&#039;");
251
+ }
252
+ function generateJsonReport(summary, violations, includeRecommendations) {
253
+ const report = {
254
+ generated_at: new Date().toISOString(),
255
+ summary,
256
+ violations: violations.map(v => ({
257
+ rule_id: v.rule_id,
258
+ category: v.category,
259
+ file: v.file,
260
+ line: v.line,
261
+ column: v.column,
262
+ impact: v.impact,
263
+ severity: impactToSeverity(v.impact),
264
+ message: v.message,
265
+ ...(includeRecommendations ? { suggestion: v.suggestion } : {}),
266
+ ...(v.code_snippet ? { code_snippet: v.code_snippet } : {})
267
+ })),
268
+ rules: {
269
+ total: getTotalRuleCount(),
270
+ by_category: getRuleCountByCategory()
271
+ }
272
+ };
273
+ return JSON.stringify(report, null, 2);
274
+ }
275
+ // ============================================================
276
+ // Main Entry Point
277
+ // ============================================================
278
+ export async function generateReport(input) {
279
+ // Run check_patterns to get violations
280
+ const checkResult = await checkPatterns({
281
+ target_paths: input.target_paths,
282
+ min_impact: "LOW" // Get all violations for the report
283
+ });
284
+ // Calculate severity counts
285
+ const bySeverity = {
286
+ error: 0,
287
+ warning: 0,
288
+ info: 0
289
+ };
290
+ const byCategory = {};
291
+ for (const v of checkResult.violations) {
292
+ const severity = impactToSeverity(v.impact);
293
+ bySeverity[severity]++;
294
+ byCategory[v.category] = (byCategory[v.category] || 0) + 1;
295
+ }
296
+ // Build summary
297
+ const summary = {
298
+ total_files_scanned: checkResult.total_files_scanned,
299
+ total_issues: checkResult.total_violations,
300
+ by_severity: bySeverity,
301
+ by_category: byCategory,
302
+ score: calculateScore(bySeverity)
303
+ };
304
+ // Generate report content
305
+ const format = input.format || "markdown";
306
+ const includeRecommendations = input.include_recommendations !== false;
307
+ let reportContent;
308
+ switch (format) {
309
+ case "html":
310
+ reportContent = generateHtmlReport(summary, checkResult.violations, includeRecommendations);
311
+ break;
312
+ case "json":
313
+ reportContent = generateJsonReport(summary, checkResult.violations, includeRecommendations);
314
+ break;
315
+ case "markdown":
316
+ default:
317
+ reportContent = generateMarkdownReport(summary, checkResult.violations, includeRecommendations);
318
+ break;
319
+ }
320
+ // Save to file if output_path is specified
321
+ let savedTo;
322
+ if (input.output_path) {
323
+ const outputPath = path.resolve(input.output_path);
324
+ const dir = path.dirname(outputPath);
325
+ if (!fs.existsSync(dir)) {
326
+ fs.mkdirSync(dir, { recursive: true });
327
+ }
328
+ fs.writeFileSync(outputPath, reportContent, "utf-8");
329
+ savedTo = outputPath;
330
+ }
331
+ return {
332
+ status: "OK",
333
+ summary,
334
+ report_content: reportContent,
335
+ ...(savedTo ? { saved_to: savedTo } : {})
336
+ };
337
+ }
@@ -0,0 +1,119 @@
1
+ // adapters/mcp-ts/src/tools/react_perf/index.ts
2
+ // react_perf.* MCP tools registration and export
3
+ import { checkPatternsInputSchema, checkPatternsOutputSchema, generateReportInputSchema, generateReportOutputSchema } from "./types.js";
4
+ import { checkPatterns } from "./check_patterns.js";
5
+ import { generateReport } from "./generate_report.js";
6
+ import { getTotalRuleCount, getRuleCountByCategory } from "./rules/index.js";
7
+ import { ToolErrorOutputSchema } from "../../generated/tool_error_output.js";
8
+ import { decorateToolOutputText, notifyDesktopBestEffort } from "../../dx/activity.js";
9
+ function toolResult(data) {
10
+ // react_perf tools are surfaced through VibePM as well; keep output decoration consistent.
11
+ const toolName = "react_perf";
12
+ const jsonText = JSON.stringify(data, null, 2);
13
+ void notifyDesktopBestEffort({ toolName, data, outcome: "success" });
14
+ return {
15
+ content: decorateToolOutputText(toolName, jsonText, data, "success"),
16
+ structuredContent: data
17
+ };
18
+ }
19
+ function err(toolName, reason, extra) {
20
+ const out = ToolErrorOutputSchema.parse({
21
+ status: "ERROR",
22
+ reason,
23
+ message: extra?.message ?? "요청을 처리하는 중 문제가 생겼습니다.",
24
+ recovery: extra?.recovery,
25
+ is_retryable: extra?.is_retryable,
26
+ details: extra?.details,
27
+ debug: extra?.debug
28
+ });
29
+ const jsonText = JSON.stringify(out, null, 2);
30
+ void notifyDesktopBestEffort({ toolName, data: out, outcome: "error" });
31
+ return {
32
+ content: decorateToolOutputText(toolName, jsonText, out, "error"),
33
+ structuredContent: out,
34
+ isError: true
35
+ };
36
+ }
37
+ // ============================================================
38
+ // Tool Handlers (with error handling)
39
+ // ============================================================
40
+ async function reactPerfCheckPatterns(input) {
41
+ const startedAt = Date.now();
42
+ try {
43
+ const result = await checkPatterns(input);
44
+ const validated = checkPatternsOutputSchema.parse(result);
45
+ const durationMs = Date.now() - startedAt;
46
+ const jsonText = JSON.stringify(validated, null, 2);
47
+ void notifyDesktopBestEffort({ toolName: "react_perf.check_patterns", data: validated, outcome: "success", durationMs });
48
+ return {
49
+ content: decorateToolOutputText("react_perf.check_patterns", jsonText, validated, "success", durationMs),
50
+ structuredContent: validated
51
+ };
52
+ }
53
+ catch (e) {
54
+ const msg = e instanceof Error ? e.message : String(e);
55
+ return err("react_perf.check_patterns", "check_patterns_failed", { message: msg });
56
+ }
57
+ }
58
+ async function reactPerfGenerateReport(input) {
59
+ const startedAt = Date.now();
60
+ try {
61
+ const result = await generateReport(input);
62
+ const validated = generateReportOutputSchema.parse(result);
63
+ const durationMs = Date.now() - startedAt;
64
+ const jsonText = JSON.stringify(validated, null, 2);
65
+ void notifyDesktopBestEffort({ toolName: "react_perf.generate_report", data: validated, outcome: "success", durationMs });
66
+ return {
67
+ content: decorateToolOutputText("react_perf.generate_report", jsonText, validated, "success", durationMs),
68
+ structuredContent: validated
69
+ };
70
+ }
71
+ catch (e) {
72
+ const msg = e instanceof Error ? e.message : String(e);
73
+ return err("react_perf.generate_report", "generate_report_failed", { message: msg });
74
+ }
75
+ }
76
+ // ============================================================
77
+ // Export
78
+ // ============================================================
79
+ export function defineReactPerfTools() {
80
+ return {
81
+ // Input schemas (for validation)
82
+ checkPatternsInputSchema,
83
+ generateReportInputSchema,
84
+ // Tool handlers
85
+ reactPerfCheckPatterns,
86
+ reactPerfGenerateReport,
87
+ // Rule info (for introspection)
88
+ getTotalRuleCount,
89
+ getRuleCountByCategory
90
+ };
91
+ }
92
+ // ============================================================
93
+ // Tool Descriptions (for MCP registration)
94
+ // ============================================================
95
+ export const REACT_PERF_TOOL_DESCRIPTIONS = {
96
+ "react_perf.check_patterns": `React/Next.js 코드에서 성능 anti-pattern을 탐지합니다.
97
+
98
+ 탐지 가능한 패턴 (48개 규칙):
99
+ - async: 순차 await (waterfall), 루프 내 await
100
+ - bundle: barrel import, 대용량 라이브러리 import
101
+ - server: Server Action 인증/검증 누락
102
+ - rendering: Suspense 바운더리, 스트리밍, Error Boundary
103
+ - client: 과도한 'use client', window 객체 접근, hydration
104
+ - rerender: 인라인 객체/함수, Context value, memo
105
+ - advanced: Parallel Routes, PPR, Optimistic Updates
106
+ - js: lodash 전체 import, moment.js, 안전하지 않은 JSON.parse
107
+
108
+ 사용 시점: 성능 최적화가 필요하거나 코드 리뷰 시`,
109
+ "react_perf.generate_report": `React/Next.js 성능 분석 리포트를 생성합니다.
110
+
111
+ 출력 형식:
112
+ - markdown: 마크다운 형식 (기본값)
113
+ - html: HTML 형식 (시각적 대시보드)
114
+ - json: JSON 형식 (자동화용)
115
+
116
+ 점수 계산: 100 - (error × 10) - (warning × 3) - (info × 1)
117
+
118
+ 사용 시점: 팀 공유용 리포트 생성, CI/CD 품질 게이트`
119
+ };