github-to-mcp-monorepo 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (388) hide show
  1. package/.env.example +8 -0
  2. package/.github/CODEOWNERS +6 -0
  3. package/.husky/pre-commit +1 -0
  4. package/.nvmrc +1 -0
  5. package/.prettierignore +5 -0
  6. package/.prettierrc +7 -0
  7. package/.vscode/settings.json +4 -0
  8. package/ARCHITECTURE.md +1429 -0
  9. package/CHANGELOG.md +167 -0
  10. package/CONTRIBUTING.md +327 -0
  11. package/LICENSE +201 -0
  12. package/README.md +1028 -0
  13. package/SECURITY.md +248 -0
  14. package/VISUAL_GUIDE.md +437 -0
  15. package/apps/vscode/IMPLEMENTATION.md +480 -0
  16. package/apps/vscode/README.md +248 -0
  17. package/apps/vscode/package.json +381 -0
  18. package/apps/vscode/resources/icon.png +0 -0
  19. package/apps/vscode/resources/icon.svg +5 -0
  20. package/apps/vscode/src/commands/browseRegistry.ts +211 -0
  21. package/apps/vscode/src/commands/configureClaudeDesktop.ts +332 -0
  22. package/apps/vscode/src/commands/convert.ts +82 -0
  23. package/apps/vscode/src/commands/convertCurrentRepo.ts +109 -0
  24. package/apps/vscode/src/commands/convertFromUrl.ts +138 -0
  25. package/apps/vscode/src/commands/index.ts +121 -0
  26. package/apps/vscode/src/commands/validate.ts +197 -0
  27. package/apps/vscode/src/extension.ts +464 -0
  28. package/apps/vscode/src/global.d.ts +36 -0
  29. package/apps/vscode/src/test/extension.test.ts +73 -0
  30. package/apps/vscode/src/utils/file-generator.ts +529 -0
  31. package/apps/vscode/src/utils/github-api.ts +335 -0
  32. package/apps/vscode/src/utils/index.ts +29 -0
  33. package/apps/vscode/src/utils/mcp-config.ts +334 -0
  34. package/apps/vscode/src/utils/storage.ts +87 -0
  35. package/apps/vscode/src/views/McpServersTreeView.ts +160 -0
  36. package/apps/vscode/src/views/OutputChannelView.ts +195 -0
  37. package/apps/vscode/src/views/StatusBarItem.ts +251 -0
  38. package/apps/vscode/src/views/ToolsExplorerView.ts +314 -0
  39. package/apps/vscode/src/views/historyProvider.ts +75 -0
  40. package/apps/vscode/src/views/index.ts +12 -0
  41. package/apps/vscode/src/views/resultsPanel.ts +330 -0
  42. package/apps/vscode/src/webviews/ConversionPanel.ts +350 -0
  43. package/apps/vscode/src/webviews/ToolDetailsPanel.ts +448 -0
  44. package/apps/vscode/src/webviews/index.ts +9 -0
  45. package/apps/vscode/src/webviews/webview-ui/styles.ts +492 -0
  46. package/apps/vscode/tsconfig.json +20 -0
  47. package/apps/web/PLAYGROUND_GUIDE.md +499 -0
  48. package/apps/web/README.md +505 -0
  49. package/apps/web/app/api/convert/route.ts +100 -0
  50. package/apps/web/app/api/convert/stream/route.ts +198 -0
  51. package/apps/web/app/api/deploy/route.ts +157 -0
  52. package/apps/web/app/api/edge/route.ts +308 -0
  53. package/apps/web/app/api/export-docker/route.ts +284 -0
  54. package/apps/web/app/api/generate-openapi/route.ts +119 -0
  55. package/apps/web/app/api/mcp/[serverId]/route.ts +263 -0
  56. package/apps/web/app/api/playground/connect/route.ts +143 -0
  57. package/apps/web/app/api/playground/disconnect/route.ts +78 -0
  58. package/apps/web/app/api/playground/execute/route.ts +135 -0
  59. package/apps/web/app/api/playground/sessions/route.ts +103 -0
  60. package/apps/web/app/api/playground/tools/route.ts +117 -0
  61. package/apps/web/app/api/playground/v2/connect/route.ts +96 -0
  62. package/apps/web/app/api/playground/v2/disconnect/route.ts +88 -0
  63. package/apps/web/app/api/playground/v2/health/route.ts +80 -0
  64. package/apps/web/app/api/playground/v2/prompts/route.ts +160 -0
  65. package/apps/web/app/api/playground/v2/resources/route.ts +159 -0
  66. package/apps/web/app/api/playground/v2/sessions/route.ts +184 -0
  67. package/apps/web/app/api/playground/v2/tools/route.ts +167 -0
  68. package/apps/web/app/api/stream/route.ts +232 -0
  69. package/apps/web/app/batch/BatchConvertClient.tsx +190 -0
  70. package/apps/web/app/batch/page.tsx +37 -0
  71. package/apps/web/app/convert/page.tsx +269 -0
  72. package/apps/web/app/dashboard/page.tsx +380 -0
  73. package/apps/web/app/globals.css +622 -0
  74. package/apps/web/app/layout.tsx +120 -0
  75. package/apps/web/app/manifest.ts +31 -0
  76. package/apps/web/app/opengraph-image.tsx +112 -0
  77. package/apps/web/app/page.old.tsx +924 -0
  78. package/apps/web/app/page.tsx +77 -0
  79. package/apps/web/app/playground/page.tsx +306 -0
  80. package/apps/web/app/playground/v2/error.tsx +163 -0
  81. package/apps/web/app/playground/v2/layout.tsx +58 -0
  82. package/apps/web/app/playground/v2/loading.tsx +152 -0
  83. package/apps/web/app/playground/v2/page.tsx +644 -0
  84. package/apps/web/app/playground/v2/providers.tsx +214 -0
  85. package/apps/web/app/playground/v2/use-shortcuts.ts +209 -0
  86. package/apps/web/app/playground/v2/use-url-state.ts +296 -0
  87. package/apps/web/app/providers.tsx +22 -0
  88. package/apps/web/app/sitemap.ts +32 -0
  89. package/apps/web/app/twitter-image.tsx +112 -0
  90. package/apps/web/components/BranchSelector.tsx +401 -0
  91. package/apps/web/components/ClaudeConfigExport.tsx +226 -0
  92. package/apps/web/components/Features.tsx +84 -0
  93. package/apps/web/components/Footer.tsx +119 -0
  94. package/apps/web/components/GenerationProgress.tsx +248 -0
  95. package/apps/web/components/GithubUrlInput.tsx +483 -0
  96. package/apps/web/components/Header.tsx +175 -0
  97. package/apps/web/components/Hero.tsx +117 -0
  98. package/apps/web/components/HowItWorks.tsx +119 -0
  99. package/apps/web/components/InstallBanner.tsx +158 -0
  100. package/apps/web/components/Logo.tsx +116 -0
  101. package/apps/web/components/ParticleBackground.tsx +105 -0
  102. package/apps/web/components/Playground.tsx +472 -0
  103. package/apps/web/components/PlaygroundToolTester.tsx +410 -0
  104. package/apps/web/components/ProductCards.tsx +179 -0
  105. package/apps/web/components/SplitView.tsx +194 -0
  106. package/apps/web/components/ToolFilter.tsx +260 -0
  107. package/apps/web/components/ToolList.tsx +325 -0
  108. package/apps/web/components/batch/BatchConvert.tsx +785 -0
  109. package/apps/web/components/batch/index.ts +7 -0
  110. package/apps/web/components/convert/ConfigTabs.tsx +230 -0
  111. package/apps/web/components/convert/ConversionResult.tsx +482 -0
  112. package/apps/web/components/convert/InlinePlayground.tsx +259 -0
  113. package/apps/web/components/convert/LoadingSteps.tsx +311 -0
  114. package/apps/web/components/convert/OneClickInstall.tsx +224 -0
  115. package/apps/web/components/convert/ToolCard.tsx +189 -0
  116. package/apps/web/components/convert/TryInPlayground.tsx +242 -0
  117. package/apps/web/components/convert/index.ts +12 -0
  118. package/apps/web/components/deploy/DeployButton.tsx +369 -0
  119. package/apps/web/components/deploy/index.ts +7 -0
  120. package/apps/web/components/docker/DockerExport.tsx +690 -0
  121. package/apps/web/components/docker/index.ts +7 -0
  122. package/apps/web/components/install/OneClickInstall.tsx +676 -0
  123. package/apps/web/components/install/index.ts +7 -0
  124. package/apps/web/components/playground/CapabilityTabs.tsx +150 -0
  125. package/apps/web/components/playground/ConnectionStatusV2.tsx +322 -0
  126. package/apps/web/components/playground/EmptyStates.tsx +305 -0
  127. package/apps/web/components/playground/ExecutionLog.tsx +260 -0
  128. package/apps/web/components/playground/ExecutionLogV2.tsx +378 -0
  129. package/apps/web/components/playground/JsonViewer.tsx +388 -0
  130. package/apps/web/components/playground/PlaygroundLayout.tsx +244 -0
  131. package/apps/web/components/playground/PromptsPanel.tsx +385 -0
  132. package/apps/web/components/playground/ResourcesPanel.tsx +378 -0
  133. package/apps/web/components/playground/SchemaForm.tsx +477 -0
  134. package/apps/web/components/playground/ServerStatus.tsx +151 -0
  135. package/apps/web/components/playground/ShareButton.tsx +239 -0
  136. package/apps/web/components/playground/ToolsPanel.tsx +309 -0
  137. package/apps/web/components/playground/TransportConfigurator.tsx +563 -0
  138. package/apps/web/components/playground/index.ts +74 -0
  139. package/apps/web/components/playground/types.ts +202 -0
  140. package/apps/web/components/streaming/StreamingProgress.tsx +441 -0
  141. package/apps/web/components/streaming/index.ts +7 -0
  142. package/apps/web/components/ui/badge.tsx +42 -0
  143. package/apps/web/components/ui/button.tsx +88 -0
  144. package/apps/web/components/ui/card.tsx +75 -0
  145. package/apps/web/components/ui/code-block.tsx +122 -0
  146. package/apps/web/components/ui/index.ts +12 -0
  147. package/apps/web/components/ui/input.tsx +55 -0
  148. package/apps/web/components/ui/tabs.tsx +61 -0
  149. package/apps/web/hooks/index.ts +85 -0
  150. package/apps/web/hooks/types.ts +1173 -0
  151. package/apps/web/hooks/use-conversion.ts +133 -0
  152. package/apps/web/hooks/use-execution-history.ts +376 -0
  153. package/apps/web/hooks/use-generation-progress.ts +147 -0
  154. package/apps/web/hooks/use-local-storage.ts +88 -0
  155. package/apps/web/hooks/use-mcp-client.ts +623 -0
  156. package/apps/web/hooks/use-mcp-connection.ts +500 -0
  157. package/apps/web/hooks/use-mcp-execution.ts +282 -0
  158. package/apps/web/hooks/use-mcp-prompts.ts +441 -0
  159. package/apps/web/hooks/use-mcp-resources.ts +430 -0
  160. package/apps/web/hooks/use-mcp-tools.ts +540 -0
  161. package/apps/web/hooks/use-playground-store.ts +299 -0
  162. package/apps/web/hooks/use-playground.ts +184 -0
  163. package/apps/web/hooks/use-streaming-conversion.ts +227 -0
  164. package/apps/web/hooks/useBatchConversion.ts +271 -0
  165. package/apps/web/hooks/useDockerConfig.ts +161 -0
  166. package/apps/web/hooks/usePlatformDetection.ts +80 -0
  167. package/apps/web/hooks/useStreaming.ts +199 -0
  168. package/apps/web/lib/api/errors.ts +386 -0
  169. package/apps/web/lib/api/index.ts +137 -0
  170. package/apps/web/lib/api/logger.ts +187 -0
  171. package/apps/web/lib/api/middleware.ts +364 -0
  172. package/apps/web/lib/api/openapi.ts +977 -0
  173. package/apps/web/lib/api/session-manager.ts +594 -0
  174. package/apps/web/lib/api/types.ts +433 -0
  175. package/apps/web/lib/api/validation.ts +523 -0
  176. package/apps/web/lib/constants.ts +114 -0
  177. package/apps/web/lib/mcp/client.ts +1137 -0
  178. package/apps/web/lib/mcp/events.ts +651 -0
  179. package/apps/web/lib/mcp/index.ts +347 -0
  180. package/apps/web/lib/mcp/logger.ts +428 -0
  181. package/apps/web/lib/mcp/metrics.ts +703 -0
  182. package/apps/web/lib/mcp/retry.ts +616 -0
  183. package/apps/web/lib/mcp/session-manager.ts +779 -0
  184. package/apps/web/lib/mcp/transports.ts +988 -0
  185. package/apps/web/lib/mcp/types.ts +594 -0
  186. package/apps/web/lib/mcp-client-enhanced.ts +871 -0
  187. package/apps/web/lib/mcp-client.ts +778 -0
  188. package/apps/web/lib/mcp-errors.ts +489 -0
  189. package/apps/web/lib/mcp-sandbox.ts +593 -0
  190. package/apps/web/lib/mcp-testing.ts +428 -0
  191. package/apps/web/lib/mcp-types.ts +448 -0
  192. package/apps/web/lib/playground-store.tsx +1147 -0
  193. package/apps/web/lib/utils.ts +439 -0
  194. package/apps/web/next-env.d.ts +5 -0
  195. package/apps/web/next.config.js +23 -0
  196. package/apps/web/package.json +55 -0
  197. package/apps/web/postcss.config.js +6 -0
  198. package/apps/web/public/.well-known/ai-plugin.json +17 -0
  199. package/apps/web/public/logo.svg +6 -0
  200. package/apps/web/public/robots.txt +22 -0
  201. package/apps/web/public/schema.json +27 -0
  202. package/apps/web/tailwind.config.js +26 -0
  203. package/apps/web/tailwind.config.ts +123 -0
  204. package/apps/web/tsconfig.json +20 -0
  205. package/apps/web/types/deploy.ts +139 -0
  206. package/apps/web/types/index.ts +247 -0
  207. package/apps/web/vercel.json +39 -0
  208. package/eslint.config.mjs +23 -0
  209. package/llms.txt +102 -0
  210. package/mkdocs/docs/api/core.md +318 -0
  211. package/mkdocs/docs/api/index.md +128 -0
  212. package/mkdocs/docs/api/mcp-server.md +301 -0
  213. package/mkdocs/docs/api/openapi-parser.md +254 -0
  214. package/mkdocs/docs/assets/logo.svg +7 -0
  215. package/mkdocs/docs/changelog.md +118 -0
  216. package/mkdocs/docs/cli/generate.md +148 -0
  217. package/mkdocs/docs/cli/index.md +52 -0
  218. package/mkdocs/docs/cli/inspect.md +164 -0
  219. package/mkdocs/docs/cli/serve.md +136 -0
  220. package/mkdocs/docs/concepts/classification.md +254 -0
  221. package/mkdocs/docs/concepts/how-it-works.md +299 -0
  222. package/mkdocs/docs/concepts/index.md +77 -0
  223. package/mkdocs/docs/concepts/mcp-protocol.md +362 -0
  224. package/mkdocs/docs/concepts/tool-types.md +382 -0
  225. package/mkdocs/docs/contributing/architecture.md +262 -0
  226. package/mkdocs/docs/contributing/development.md +245 -0
  227. package/mkdocs/docs/contributing/index.md +73 -0
  228. package/mkdocs/docs/contributing/testing.md +320 -0
  229. package/mkdocs/docs/getting-started/configuration.md +235 -0
  230. package/mkdocs/docs/getting-started/index.md +54 -0
  231. package/mkdocs/docs/getting-started/installation.md +145 -0
  232. package/mkdocs/docs/getting-started/quickstart.md +160 -0
  233. package/mkdocs/docs/guides/batch.md +375 -0
  234. package/mkdocs/docs/guides/claude-desktop.md +227 -0
  235. package/mkdocs/docs/guides/cursor.md +188 -0
  236. package/mkdocs/docs/guides/custom-tools.md +367 -0
  237. package/mkdocs/docs/guides/index.md +78 -0
  238. package/mkdocs/docs/guides/private-repos.md +221 -0
  239. package/mkdocs/docs/guides/vscode.md +247 -0
  240. package/mkdocs/docs/index.md +175 -0
  241. package/mkdocs/docs/reference/config.md +223 -0
  242. package/mkdocs/docs/reference/env.md +192 -0
  243. package/mkdocs/docs/reference/index.md +102 -0
  244. package/mkdocs/docs/reference/tools.md +309 -0
  245. package/mkdocs/docs/stylesheets/extra.css +231 -0
  246. package/mkdocs/mkdocs.yml +204 -0
  247. package/mkdocs/overrides/.gitkeep +1 -0
  248. package/mkdocs/overrides/main.html +7 -0
  249. package/mkdocs/python-deps.txt +7 -0
  250. package/mkdocs/vercel.json +11 -0
  251. package/package.json +63 -0
  252. package/packages/core/package.json +61 -0
  253. package/packages/core/src/__tests__/bitbucket-client.test.ts +366 -0
  254. package/packages/core/src/__tests__/cli.test.ts +235 -0
  255. package/packages/core/src/__tests__/code-extractor.test.ts +378 -0
  256. package/packages/core/src/__tests__/docker-generator.test.ts +255 -0
  257. package/packages/core/src/__tests__/github-client.test.ts +390 -0
  258. package/packages/core/src/__tests__/gitlab-client.test.ts +319 -0
  259. package/packages/core/src/__tests__/go-extractor.test.ts +351 -0
  260. package/packages/core/src/__tests__/graphql-extractor.test.ts +330 -0
  261. package/packages/core/src/__tests__/java-extractor.test.ts +497 -0
  262. package/packages/core/src/__tests__/plugins.test.ts +467 -0
  263. package/packages/core/src/__tests__/readme-extractor.test.ts +258 -0
  264. package/packages/core/src/__tests__/redis-cache.test.ts +307 -0
  265. package/packages/core/src/__tests__/rust-extractor.test.ts +252 -0
  266. package/packages/core/src/__tests__/streaming.test.ts +251 -0
  267. package/packages/core/src/additional-extractors.ts +333 -0
  268. package/packages/core/src/cache/cache-interface.ts +179 -0
  269. package/packages/core/src/cache/index.ts +210 -0
  270. package/packages/core/src/cache/redis-cache.ts +291 -0
  271. package/packages/core/src/cache/upstash-cache.ts +379 -0
  272. package/packages/core/src/cache.ts +251 -0
  273. package/packages/core/src/cli.ts +822 -0
  274. package/packages/core/src/code-extractor.ts +696 -0
  275. package/packages/core/src/docker-generator.ts +470 -0
  276. package/packages/core/src/edge-compatible.ts +491 -0
  277. package/packages/core/src/extractors/go-extractor.ts +791 -0
  278. package/packages/core/src/extractors/index.ts +9 -0
  279. package/packages/core/src/extractors/java-extractor.ts +937 -0
  280. package/packages/core/src/extractors/rust-extractor.ts +744 -0
  281. package/packages/core/src/github-client.ts +319 -0
  282. package/packages/core/src/go-generator.ts +356 -0
  283. package/packages/core/src/graphql-extractor.ts +358 -0
  284. package/packages/core/src/index.ts +797 -0
  285. package/packages/core/src/langchain-exporter.ts +617 -0
  286. package/packages/core/src/language-parsers.ts +1114 -0
  287. package/packages/core/src/mcp-introspector.ts +279 -0
  288. package/packages/core/src/monorepo-detector.ts +378 -0
  289. package/packages/core/src/plugins/index.ts +370 -0
  290. package/packages/core/src/plugins/registry.ts +404 -0
  291. package/packages/core/src/plugins/types.ts +215 -0
  292. package/packages/core/src/providers/base-provider.ts +246 -0
  293. package/packages/core/src/providers/bitbucket-client.ts +464 -0
  294. package/packages/core/src/providers/gitlab-client.ts +388 -0
  295. package/packages/core/src/providers/index.ts +176 -0
  296. package/packages/core/src/python-generator.ts +260 -0
  297. package/packages/core/src/queue/index.ts +100 -0
  298. package/packages/core/src/queue/memory-queue.ts +445 -0
  299. package/packages/core/src/queue/redis-queue.ts +578 -0
  300. package/packages/core/src/queue/types.ts +251 -0
  301. package/packages/core/src/readme-extractor.ts +409 -0
  302. package/packages/core/src/schema-generator.ts +638 -0
  303. package/packages/core/src/streaming.ts +999 -0
  304. package/packages/core/src/types.ts +289 -0
  305. package/packages/core/tsconfig.json +9 -0
  306. package/packages/core/tsup.config.ts +25 -0
  307. package/packages/mcp-server/README.md +297 -0
  308. package/packages/mcp-server/package.json +55 -0
  309. package/packages/mcp-server/src/__tests__/mcp-server.test.ts +177 -0
  310. package/packages/mcp-server/src/__tests__/tools.test.ts +217 -0
  311. package/packages/mcp-server/src/index.ts +1206 -0
  312. package/packages/mcp-server/src/prompts/index.ts +601 -0
  313. package/packages/mcp-server/src/tools/export-docker.ts +362 -0
  314. package/packages/mcp-server/src/tools/generate-openapi.ts +162 -0
  315. package/packages/mcp-server/src/tools/monitor-mcp-server.ts +448 -0
  316. package/packages/mcp-server/src/tools/stream-convert.ts +398 -0
  317. package/packages/mcp-server/src/tools/test-mcp-tool.ts +531 -0
  318. package/packages/mcp-server/tsconfig.json +12 -0
  319. package/packages/mcp-server/tsup.config.ts +14 -0
  320. package/packages/openapi-parser/package-lock.json +3028 -0
  321. package/packages/openapi-parser/package.json +41 -0
  322. package/packages/openapi-parser/src/analyzer.ts +700 -0
  323. package/packages/openapi-parser/src/asyncapi-parser.ts +475 -0
  324. package/packages/openapi-parser/src/cli.ts +302 -0
  325. package/packages/openapi-parser/src/generator.ts +570 -0
  326. package/packages/openapi-parser/src/generators/express-analyzer.ts +649 -0
  327. package/packages/openapi-parser/src/generators/fastapi-analyzer.ts +960 -0
  328. package/packages/openapi-parser/src/generators/index.ts +200 -0
  329. package/packages/openapi-parser/src/generators/nextjs-analyzer.ts +768 -0
  330. package/packages/openapi-parser/src/generators/openapi-builder.ts +527 -0
  331. package/packages/openapi-parser/src/generators/types.ts +298 -0
  332. package/packages/openapi-parser/src/graphql-parser.ts +462 -0
  333. package/packages/openapi-parser/src/grpc-parser.ts +649 -0
  334. package/packages/openapi-parser/src/har-parser.ts +723 -0
  335. package/packages/openapi-parser/src/index.ts +635 -0
  336. package/packages/openapi-parser/src/insomnia-parser.ts +614 -0
  337. package/packages/openapi-parser/src/parser.ts +231 -0
  338. package/packages/openapi-parser/src/postman-parser.ts +611 -0
  339. package/packages/openapi-parser/src/ref-resolver.ts +313 -0
  340. package/packages/openapi-parser/src/transformer.ts +459 -0
  341. package/packages/openapi-parser/tests/generators/express.test.ts +209 -0
  342. package/packages/openapi-parser/tests/generators/fastapi.test.ts +236 -0
  343. package/packages/openapi-parser/tests/generators/nextjs.test.ts +273 -0
  344. package/packages/openapi-parser/tests/parsers.test.ts +847 -0
  345. package/packages/openapi-parser/tsconfig.json +9 -0
  346. package/packages/openapi-parser/tsup.config.ts +11 -0
  347. package/packages/registry/package.json +59 -0
  348. package/packages/registry/src/cli.ts +456 -0
  349. package/packages/registry/src/index.ts +44 -0
  350. package/packages/registry/src/popular/github.json +47 -0
  351. package/packages/registry/src/popular/index.ts +55 -0
  352. package/packages/registry/src/popular/linear.json +42 -0
  353. package/packages/registry/src/popular/notion.json +42 -0
  354. package/packages/registry/src/popular/openai.json +40 -0
  355. package/packages/registry/src/popular/resend.json +38 -0
  356. package/packages/registry/src/popular/slack.json +42 -0
  357. package/packages/registry/src/popular/stripe.json +163 -0
  358. package/packages/registry/src/popular/supabase.json +42 -0
  359. package/packages/registry/src/popular/twilio.json +40 -0
  360. package/packages/registry/src/popular/vercel.json +40 -0
  361. package/packages/registry/src/registry.ts +492 -0
  362. package/packages/registry/src/storage.ts +334 -0
  363. package/packages/registry/src/types.ts +275 -0
  364. package/packages/registry/src/updater.ts +208 -0
  365. package/packages/registry/tsconfig.json +10 -0
  366. package/packages/registry/tsup.config.ts +11 -0
  367. package/pnpm-workspace.yaml +3 -0
  368. package/scripts/build-docs.sh +16 -0
  369. package/server.json +9 -0
  370. package/templates/Dockerfile.python.template +60 -0
  371. package/templates/Dockerfile.typescript.template +60 -0
  372. package/templates/docker-compose.template.yml +68 -0
  373. package/tests/fixtures/express-app/index.js +34 -0
  374. package/tests/fixtures/express-app/routes/posts.js +43 -0
  375. package/tests/fixtures/express-app/routes/users.js +58 -0
  376. package/tests/fixtures/fastapi-app/main.py +125 -0
  377. package/tests/fixtures/fastapi-app/routes/admin.py +42 -0
  378. package/tests/fixtures/graphql/simple-schema.graphql +65 -0
  379. package/tests/fixtures/mocks/github-api-responses.json +63 -0
  380. package/tests/fixtures/nextjs-app/app/api/posts/route.ts +55 -0
  381. package/tests/fixtures/nextjs-app/app/api/users/[id]/route.ts +63 -0
  382. package/tests/fixtures/nextjs-app/app/api/users/route.ts +44 -0
  383. package/tests/fixtures/nextjs-app/pages/api/health.ts +28 -0
  384. package/tests/fixtures/openapi/petstore.yaml +179 -0
  385. package/tests/integration/langchain-export.test.ts +405 -0
  386. package/tests/integration/openapi-conversion.test.ts +221 -0
  387. package/tsconfig.json +18 -0
  388. package/vitest.config.ts +32 -0
@@ -0,0 +1,483 @@
1
+ 'use client'
2
+
3
+ import { useState, useCallback, useEffect, useMemo } from 'react'
4
+ import { motion, AnimatePresence } from 'framer-motion'
5
+ import { Search, ArrowRight, AlertCircle, X, Loader2, GitBranch, CheckCircle2, Lightbulb } from 'lucide-react'
6
+ import BranchSelector, { type GitRef } from './BranchSelector'
7
+
8
+ interface GithubUrlInputProps {
9
+ onSubmit: (url: string, ref?: GitRef | null) => void
10
+ disabled?: boolean
11
+ initialValue?: string
12
+ showBranchSelector?: boolean
13
+ }
14
+
15
+ interface ValidationResult {
16
+ isValid: boolean
17
+ error?: string
18
+ suggestion?: string
19
+ correctedUrl?: string
20
+ hint?: string
21
+ }
22
+
23
+ /**
24
+ * Comprehensive GitHub URL validation with auto-correction and helpful suggestions
25
+ */
26
+ function validateGithubUrl(input: string): ValidationResult {
27
+ const trimmed = input.trim()
28
+
29
+ // Empty check
30
+ if (!trimmed) {
31
+ return {
32
+ isValid: false,
33
+ error: 'Please enter a GitHub URL',
34
+ hint: 'Example: github.com/facebook/react'
35
+ }
36
+ }
37
+
38
+ // Check for common non-GitHub URLs and provide specific guidance
39
+ const nonGithubPatterns = [
40
+ { pattern: /gitlab\.com/i, name: 'GitLab', suggestion: 'This tool only supports GitHub repositories. GitLab support coming soon!' },
41
+ { pattern: /bitbucket\.org/i, name: 'Bitbucket', suggestion: 'This tool only supports GitHub repositories.' },
42
+ { pattern: /npmjs\.com|npm\.im/i, name: 'npm', suggestion: 'Try entering the GitHub repository URL instead. Check the npm page for the repo link.' },
43
+ { pattern: /pypi\.org/i, name: 'PyPI', suggestion: 'Try entering the GitHub repository URL instead. Check the PyPI page for the repo link.' },
44
+ { pattern: /stackoverflow\.com|stackexchange\.com/i, name: 'Stack Overflow', suggestion: 'Please enter a GitHub repository URL, not a Q&A link.' },
45
+ { pattern: /google\.com|bing\.com|duckduckgo\.com/i, name: 'Search engine', suggestion: 'Please enter a GitHub repository URL directly.' },
46
+ { pattern: /youtube\.com|youtu\.be/i, name: 'YouTube', suggestion: 'Please enter a GitHub repository URL, not a video link.' },
47
+ { pattern: /twitter\.com|x\.com/i, name: 'Twitter/X', suggestion: 'Please enter a GitHub repository URL, not a social media link.' },
48
+ { pattern: /medium\.com|dev\.to/i, name: 'Blog', suggestion: 'Please enter a GitHub repository URL, not a blog post.' },
49
+ ]
50
+
51
+ for (const { pattern, name, suggestion } of nonGithubPatterns) {
52
+ if (pattern.test(trimmed)) {
53
+ return {
54
+ isValid: false,
55
+ error: `This looks like a ${name} URL`,
56
+ suggestion,
57
+ hint: 'Example: github.com/owner/repo'
58
+ }
59
+ }
60
+ }
61
+
62
+ // Try to normalize and parse the URL
63
+ let normalizedUrl = trimmed
64
+
65
+ // Auto-correct common mistakes
66
+ const corrections: Array<{ pattern: RegExp; replacement: string; description: string }> = [
67
+ // Fix github.com typos
68
+ { pattern: /^(https?:\/\/)?(www\.)?githib\.com/i, replacement: 'https://github.com', description: 'Fixed typo: githib → github' },
69
+ { pattern: /^(https?:\/\/)?(www\.)?gihub\.com/i, replacement: 'https://github.com', description: 'Fixed typo: gihub → github' },
70
+ { pattern: /^(https?:\/\/)?(www\.)?guthub\.com/i, replacement: 'https://github.com', description: 'Fixed typo: guthub → github' },
71
+ { pattern: /^(https?:\/\/)?(www\.)?githuub\.com/i, replacement: 'https://github.com', description: 'Fixed typo: githuub → github' },
72
+ // Remove trailing .git
73
+ { pattern: /\.git\/?$/i, replacement: '', description: 'Removed .git suffix' },
74
+ // Fix double slashes (except after protocol)
75
+ { pattern: /([^:])\/\//g, replacement: '$1/', description: 'Fixed double slashes' },
76
+ ]
77
+
78
+ let wasAutoCorreted = false
79
+ let correctionDescription = ''
80
+
81
+ for (const { pattern, replacement, description } of corrections) {
82
+ if (pattern.test(normalizedUrl)) {
83
+ normalizedUrl = normalizedUrl.replace(pattern, replacement)
84
+ wasAutoCorreted = true
85
+ correctionDescription = description
86
+ }
87
+ }
88
+
89
+ // Add https:// if missing
90
+ if (!normalizedUrl.startsWith('http://') && !normalizedUrl.startsWith('https://')) {
91
+ normalizedUrl = 'https://' + normalizedUrl
92
+ }
93
+
94
+ // Try to parse as URL
95
+ let parsedUrl: URL
96
+ try {
97
+ parsedUrl = new URL(normalizedUrl)
98
+ } catch {
99
+ return {
100
+ isValid: false,
101
+ error: 'Invalid URL format',
102
+ hint: 'Try: github.com/owner/repo'
103
+ }
104
+ }
105
+
106
+ // Check hostname
107
+ const validHostnames = ['github.com', 'www.github.com']
108
+ if (!validHostnames.includes(parsedUrl.hostname.toLowerCase())) {
109
+ // Check if it might be a raw GitHub content URL
110
+ if (parsedUrl.hostname === 'raw.githubusercontent.com') {
111
+ const parts = parsedUrl.pathname.split('/').filter(Boolean)
112
+ if (parts.length >= 2) {
113
+ const correctedUrl = `https://github.com/${parts[0]}/${parts[1]}`
114
+ return {
115
+ isValid: false,
116
+ error: 'This is a raw content URL',
117
+ suggestion: 'Use the repository URL instead',
118
+ correctedUrl,
119
+ hint: `Did you mean: ${correctedUrl}?`
120
+ }
121
+ }
122
+ }
123
+
124
+ // Check for gist URLs
125
+ if (parsedUrl.hostname === 'gist.github.com') {
126
+ return {
127
+ isValid: false,
128
+ error: 'Gist URLs are not supported',
129
+ suggestion: 'Please enter a GitHub repository URL, not a Gist.',
130
+ hint: 'Example: github.com/owner/repo'
131
+ }
132
+ }
133
+
134
+ return {
135
+ isValid: false,
136
+ error: 'Not a GitHub URL',
137
+ suggestion: 'Enter a URL starting with github.com',
138
+ hint: 'Example: github.com/owner/repo'
139
+ }
140
+ }
141
+
142
+ // Parse path parts
143
+ const pathParts = parsedUrl.pathname.split('/').filter(Boolean)
144
+
145
+ // Check for owner/repo pattern
146
+ if (pathParts.length === 0) {
147
+ return {
148
+ isValid: false,
149
+ error: 'Missing repository path',
150
+ suggestion: 'Add the owner and repository name',
151
+ hint: 'Format: github.com/owner/repo'
152
+ }
153
+ }
154
+
155
+ if (pathParts.length === 1) {
156
+ // Only owner provided
157
+ return {
158
+ isValid: false,
159
+ error: 'Missing repository name',
160
+ suggestion: `Add the repository name after ${pathParts[0]}`,
161
+ hint: `Example: github.com/${pathParts[0]}/repository-name`
162
+ }
163
+ }
164
+
165
+ const owner = pathParts[0]
166
+ let repo = pathParts[1]
167
+
168
+ // Validate owner format (GitHub usernames: alphanumeric and hyphens, can't start/end with hyphen)
169
+ if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(owner) || owner.length > 39) {
170
+ return {
171
+ isValid: false,
172
+ error: 'Invalid owner/organization name',
173
+ hint: 'Owner names can only contain letters, numbers, and hyphens'
174
+ }
175
+ }
176
+
177
+ // Validate repo format (can contain letters, numbers, hyphens, underscores, dots)
178
+ // Remove any extra path parts (like /tree/main)
179
+ repo = repo.replace(/\.git$/, '')
180
+ if (!/^[a-zA-Z0-9._-]+$/.test(repo) || repo.length > 100) {
181
+ return {
182
+ isValid: false,
183
+ error: 'Invalid repository name',
184
+ hint: 'Repository names can only contain letters, numbers, dots, hyphens, and underscores'
185
+ }
186
+ }
187
+
188
+ // Build the canonical URL
189
+ const canonicalUrl = `https://github.com/${owner}/${repo}`
190
+
191
+ // Success!
192
+ if (wasAutoCorreted) {
193
+ return {
194
+ isValid: true,
195
+ correctedUrl: canonicalUrl,
196
+ suggestion: correctionDescription
197
+ }
198
+ }
199
+
200
+ return { isValid: true, correctedUrl: canonicalUrl }
201
+ }
202
+
203
+ function parseGithubUrl(urlString: string): { owner: string; repo: string } | null {
204
+ const result = validateGithubUrl(urlString)
205
+ if (!result.isValid || !result.correctedUrl) return null
206
+
207
+ try {
208
+ const url = new URL(result.correctedUrl)
209
+ const pathParts = url.pathname.split('/').filter(Boolean)
210
+ if (pathParts.length >= 2) {
211
+ return { owner: pathParts[0], repo: pathParts[1].replace(/\.git$/, '') }
212
+ }
213
+ } catch {
214
+ // ignore
215
+ }
216
+ return null
217
+ }
218
+
219
+ export default function GithubUrlInput({ onSubmit, disabled = false, initialValue = '', showBranchSelector = false }: GithubUrlInputProps) {
220
+ const [url, setUrl] = useState(initialValue)
221
+ const [isFocused, setIsFocused] = useState(false)
222
+ const [error, setError] = useState<string | null>(null)
223
+ const [suggestion, setSuggestion] = useState<string | null>(null)
224
+ const [hint, setHint] = useState<string | null>(null)
225
+ const [correctedUrl, setCorrectedUrl] = useState<string | null>(null)
226
+ const [selectedRef, setSelectedRef] = useState<GitRef | null>(null)
227
+
228
+ // Parse owner/repo from URL for BranchSelector
229
+ const repoInfo = useMemo(() => parseGithubUrl(url), [url])
230
+
231
+ // Live validation as user types (debounced feel with useMemo)
232
+ const liveValidation = useMemo(() => {
233
+ if (!url.trim()) return null
234
+ return validateGithubUrl(url)
235
+ }, [url])
236
+
237
+ // Update url when initialValue changes
238
+ useEffect(() => {
239
+ if (initialValue) {
240
+ setUrl(initialValue)
241
+ }
242
+ }, [initialValue])
243
+
244
+ const handleSubmit = useCallback((e: React.FormEvent) => {
245
+ e.preventDefault()
246
+
247
+ const validation = validateGithubUrl(url)
248
+
249
+ if (!validation.isValid) {
250
+ setError(validation.error || 'Invalid URL')
251
+ setSuggestion(validation.suggestion || null)
252
+ setHint(validation.hint || null)
253
+ setCorrectedUrl(validation.correctedUrl || null)
254
+ return
255
+ }
256
+
257
+ // Clear errors and submit with the corrected URL
258
+ setError(null)
259
+ setSuggestion(null)
260
+ setHint(null)
261
+ setCorrectedUrl(null)
262
+ onSubmit(validation.correctedUrl || url.trim(), selectedRef)
263
+ }, [url, onSubmit, selectedRef])
264
+
265
+ const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
266
+ setUrl(e.target.value)
267
+ // Clear errors on change
268
+ if (error) {
269
+ setError(null)
270
+ setSuggestion(null)
271
+ setHint(null)
272
+ setCorrectedUrl(null)
273
+ }
274
+ }, [error])
275
+
276
+ const handleClear = useCallback(() => {
277
+ setUrl('')
278
+ setError(null)
279
+ setSuggestion(null)
280
+ setHint(null)
281
+ setCorrectedUrl(null)
282
+ setSelectedRef(null)
283
+ }, [])
284
+
285
+ const handleUseCorrectedUrl = useCallback(() => {
286
+ if (correctedUrl) {
287
+ setUrl(correctedUrl)
288
+ setError(null)
289
+ setSuggestion(null)
290
+ setHint(null)
291
+ setCorrectedUrl(null)
292
+ }
293
+ }, [correctedUrl])
294
+
295
+ // Show valid indicator
296
+ const showValidIndicator = liveValidation?.isValid && url.trim().length > 0
297
+
298
+ return (
299
+ <div className="relative">
300
+ {/* Glow effect */}
301
+ <div
302
+ className={`absolute -inset-2 bg-white/10 rounded-2xl blur-xl transition-opacity duration-500 ${isFocused ? 'opacity-100' : 'opacity-0'}`}
303
+ aria-hidden="true"
304
+ />
305
+
306
+ <form onSubmit={handleSubmit} className="relative">
307
+ {/* Mobile-first layout: stacked on small screens, inline on larger */}
308
+ <div className="flex flex-col sm:flex-row gap-3 sm:gap-0 relative">
309
+ {/* Input container */}
310
+ <div className="relative flex-1">
311
+ <div className="absolute left-4 top-1/2 -translate-y-1/2 pointer-events-none">
312
+ <Search className="w-5 h-5 text-neutral-500" aria-hidden="true" />
313
+ </div>
314
+ <input
315
+ type="text"
316
+ value={url}
317
+ onChange={handleChange}
318
+ onFocus={() => setIsFocused(true)}
319
+ onBlur={() => setIsFocused(false)}
320
+ placeholder="Enter GitHub repo URL (e.g., github.com/owner/repo)"
321
+ aria-label="GitHub Repository URL"
322
+ aria-describedby={error ? 'url-error' : undefined}
323
+ aria-invalid={error ? 'true' : 'false'}
324
+ disabled={disabled}
325
+ className={`w-full pl-12 pr-12 sm:pr-36 py-4 bg-black border rounded-xl text-white placeholder:text-neutral-500 focus:outline-none focus:ring-2 transition-all text-base sm:text-lg min-h-[56px] disabled:opacity-50 disabled:cursor-not-allowed ${
326
+ error
327
+ ? 'border-red-500 focus:border-red-500 focus:ring-red-500/20'
328
+ : showValidIndicator
329
+ ? 'border-green-500 focus:border-green-500 focus:ring-green-500/20'
330
+ : 'border-neutral-700 focus:border-neutral-500 focus:ring-white/10'
331
+ }`}
332
+ />
333
+ {/* Valid indicator / Clear button container */}
334
+ <div className="absolute right-3 sm:right-28 top-1/2 -translate-y-1/2 flex items-center gap-1">
335
+ {/* Valid indicator */}
336
+ {showValidIndicator && !error && (
337
+ <motion.div
338
+ initial={{ scale: 0 }}
339
+ animate={{ scale: 1 }}
340
+ className="p-1 text-green-500"
341
+ title="Valid GitHub URL"
342
+ >
343
+ <CheckCircle2 className="w-4 h-4" />
344
+ </motion.div>
345
+ )}
346
+ {/* Clear button */}
347
+ {url && (
348
+ <button
349
+ type="button"
350
+ onClick={handleClear}
351
+ className="p-1.5 text-neutral-500 hover:text-white transition-colors rounded-md focus:outline-none focus:ring-2 focus:ring-white/20"
352
+ aria-label="Clear input"
353
+ >
354
+ <X className="w-4 h-4" />
355
+ </button>
356
+ )}
357
+ </div>
358
+ {/* Desktop submit button (inside input) */}
359
+ <button
360
+ type="submit"
361
+ disabled={!url.trim() || disabled}
362
+ className="hidden sm:flex absolute right-2 top-1/2 -translate-y-1/2 px-5 py-2.5 bg-white rounded-lg font-semibold text-black items-center gap-2 hover:bg-neutral-200 transition-all disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-white/50 min-h-[44px]"
363
+ >
364
+ {disabled ? (
365
+ <>
366
+ <Loader2 className="w-4 h-4 animate-spin" aria-hidden="true" />
367
+ Converting...
368
+ </>
369
+ ) : (
370
+ <>
371
+ Convert
372
+ <ArrowRight className="w-4 h-4" aria-hidden="true" />
373
+ </>
374
+ )}
375
+ </button>
376
+ </div>
377
+
378
+ {/* Mobile submit button (separate, full width) */}
379
+ <button
380
+ type="submit"
381
+ disabled={!url.trim() || disabled}
382
+ className="sm:hidden flex items-center justify-center gap-2 w-full px-6 py-4 bg-white rounded-xl font-semibold text-black hover:bg-neutral-200 transition-all disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-white/50 min-h-[56px] text-base"
383
+ >
384
+ {disabled ? (
385
+ <>
386
+ <Loader2 className="w-5 h-5 animate-spin" aria-hidden="true" />
387
+ Converting...
388
+ </>
389
+ ) : (
390
+ <>
391
+ Convert to MCP
392
+ <ArrowRight className="w-5 h-5" aria-hidden="true" />
393
+ </>
394
+ )}
395
+ </button>
396
+ </div>
397
+
398
+ {/* Branch selector */}
399
+ {showBranchSelector && repoInfo && (
400
+ <motion.div
401
+ initial={{ opacity: 0, y: -10 }}
402
+ animate={{ opacity: 1, y: 0 }}
403
+ className="mt-3 flex items-center gap-2"
404
+ >
405
+ <GitBranch className="w-4 h-4 text-neutral-500" />
406
+ <span className="text-sm text-neutral-500">Branch/Tag:</span>
407
+ <BranchSelector
408
+ owner={repoInfo.owner}
409
+ repo={repoInfo.repo}
410
+ selectedRef={selectedRef}
411
+ onRefChange={setSelectedRef}
412
+ disabled={disabled}
413
+ />
414
+ </motion.div>
415
+ )}
416
+
417
+ {/* Enhanced error message with suggestions */}
418
+ <AnimatePresence>
419
+ {(error || suggestion || hint || correctedUrl) && (
420
+ <motion.div
421
+ id="url-error"
422
+ role="alert"
423
+ initial={{ opacity: 0, y: -5, height: 0 }}
424
+ animate={{ opacity: 1, y: 0, height: 'auto' }}
425
+ exit={{ opacity: 0, y: -5, height: 0 }}
426
+ transition={{ duration: 0.2 }}
427
+ className="mt-3 p-3 bg-neutral-900 border border-neutral-800 rounded-lg"
428
+ >
429
+ {/* Error message */}
430
+ {error && (
431
+ <div className="flex items-center gap-2 text-red-400 text-sm">
432
+ <AlertCircle className="w-4 h-4 flex-shrink-0" aria-hidden="true" />
433
+ <span className="font-medium">{error}</span>
434
+ </div>
435
+ )}
436
+
437
+ {/* Suggestion */}
438
+ {suggestion && (
439
+ <p className="mt-1.5 text-neutral-400 text-sm pl-6">
440
+ {suggestion}
441
+ </p>
442
+ )}
443
+
444
+ {/* Hint with example */}
445
+ {hint && (
446
+ <div className="mt-2 pl-6 flex items-center gap-2 text-neutral-500 text-sm">
447
+ <Lightbulb className="w-3.5 h-3.5 flex-shrink-0 text-yellow-500/70" />
448
+ <span>{hint}</span>
449
+ </div>
450
+ )}
451
+
452
+ {/* Corrected URL suggestion button */}
453
+ {correctedUrl && (
454
+ <button
455
+ type="button"
456
+ onClick={handleUseCorrectedUrl}
457
+ className="mt-2 ml-6 px-3 py-1.5 bg-white/10 hover:bg-white/20 text-white text-sm rounded-md transition-colors flex items-center gap-2"
458
+ >
459
+ <span>Use: </span>
460
+ <code className="text-green-400">{correctedUrl}</code>
461
+ </button>
462
+ )}
463
+ </motion.div>
464
+ )}
465
+ </AnimatePresence>
466
+
467
+ {/* Live validation hint (when typing, before submit) */}
468
+ <AnimatePresence>
469
+ {!error && liveValidation?.correctedUrl && liveValidation.correctedUrl !== url && url.trim().length > 0 && (
470
+ <motion.div
471
+ initial={{ opacity: 0, y: -5 }}
472
+ animate={{ opacity: 1, y: 0 }}
473
+ exit={{ opacity: 0, y: -5 }}
474
+ className="mt-2 text-neutral-500 text-xs pl-1"
475
+ >
476
+ Will convert as: <span className="text-neutral-400">{liveValidation.correctedUrl}</span>
477
+ </motion.div>
478
+ )}
479
+ </AnimatePresence>
480
+ </form>
481
+ </div>
482
+ )
483
+ }
@@ -0,0 +1,175 @@
1
+ 'use client'
2
+
3
+ import { useState, useEffect, useCallback } from 'react'
4
+ import { motion, AnimatePresence } from 'framer-motion'
5
+ import { Menu, X, Terminal, Layers, BookOpen, Cloud, Zap, Sparkles } from 'lucide-react'
6
+ import Link from 'next/link'
7
+ import { Logo } from './Logo'
8
+
9
+ const NAV_ITEMS = [
10
+ { href: '/#how-it-works', label: 'How it works', icon: Zap },
11
+ { href: '/#features', label: 'Features', icon: Sparkles },
12
+ { href: '/playground/v2', label: 'Playground', icon: Terminal, badge: 'v2' },
13
+ { href: '/batch', label: 'Batch Convert', icon: Layers },
14
+ { href: '/dashboard', label: 'Dashboard', icon: Cloud },
15
+ ]
16
+
17
+ export default function Header() {
18
+ const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
19
+
20
+ // Close mobile menu on escape key
21
+ useEffect(() => {
22
+ const handleEscape = (e: KeyboardEvent) => {
23
+ if (e.key === 'Escape') setMobileMenuOpen(false)
24
+ }
25
+ document.addEventListener('keydown', handleEscape)
26
+ return () => document.removeEventListener('keydown', handleEscape)
27
+ }, [])
28
+
29
+ // Close mobile menu when clicking outside
30
+ const handleOverlayClick = useCallback(() => {
31
+ setMobileMenuOpen(false)
32
+ }, [])
33
+
34
+ // Prevent body scroll when menu is open
35
+ useEffect(() => {
36
+ if (mobileMenuOpen) {
37
+ document.body.style.overflow = 'hidden'
38
+ } else {
39
+ document.body.style.overflow = ''
40
+ }
41
+ return () => {
42
+ document.body.style.overflow = ''
43
+ }
44
+ }, [mobileMenuOpen])
45
+
46
+ return (
47
+ <motion.header
48
+ initial={{ y: -100 }}
49
+ animate={{ y: 0 }}
50
+ className="fixed top-0 left-0 right-0 z-50 backdrop-blur-xl bg-black/80 border-b border-neutral-800"
51
+ >
52
+ <div className="container mx-auto px-4">
53
+ <div className="flex items-center justify-between h-16">
54
+ {/* Logo */}
55
+ <Link
56
+ href="/"
57
+ className="flex items-center group focus:outline-none focus:ring-2 focus:ring-white/20 rounded-lg p-1 -ml-1"
58
+ aria-label="github-to-mcp home"
59
+ >
60
+ <Logo size="md" />
61
+ </Link>
62
+
63
+ {/* Desktop Nav */}
64
+ <nav className="hidden md:flex items-center gap-8 text-sm" aria-label="Main navigation">
65
+ {NAV_ITEMS.map((item) => (
66
+ <Link
67
+ key={item.href}
68
+ href={item.href}
69
+ className="flex items-center gap-1.5 text-neutral-400 hover:text-white transition-colors focus:outline-none focus:ring-2 focus:ring-white/20 rounded px-2 py-1 -mx-2 -my-1"
70
+ >
71
+ {item.icon && <item.icon className="w-4 h-4" />}
72
+ {item.label}
73
+ {'badge' in item && item.badge && (
74
+ <span className="ml-1 px-1.5 py-0.5 text-[10px] font-medium bg-green-500/20 text-green-400 rounded-full">
75
+ {item.badge}
76
+ </span>
77
+ )}
78
+ </Link>
79
+ ))}
80
+ </nav>
81
+
82
+ {/* Right side - Desktop */}
83
+ <div className="hidden md:flex items-center gap-3">
84
+ <a
85
+ href="https://github.com/nirholas/github-to-mcp"
86
+ target="_blank"
87
+ rel="noopener noreferrer"
88
+ className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-neutral-400 hover:text-white bg-transparent border border-neutral-700 rounded-lg hover:border-neutral-600 hover:bg-white/5 transition-colors focus:outline-none focus:ring-2 focus:ring-white/20"
89
+ aria-label="View on GitHub"
90
+ >
91
+ <svg className="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
92
+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/>
93
+ </svg>
94
+ <span>GitHub</span>
95
+ </a>
96
+ </div>
97
+
98
+ {/* Mobile menu button */}
99
+ <button
100
+ onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
101
+ className="md:hidden flex items-center justify-center w-11 h-11 -mr-2 text-neutral-400 hover:text-white transition-colors focus:outline-none focus:ring-2 focus:ring-white/20 rounded-lg"
102
+ aria-expanded={mobileMenuOpen}
103
+ aria-controls="mobile-menu"
104
+ aria-label={mobileMenuOpen ? 'Close menu' : 'Open menu'}
105
+ >
106
+ {mobileMenuOpen ? (
107
+ <X className="w-6 h-6" />
108
+ ) : (
109
+ <Menu className="w-6 h-6" />
110
+ )}
111
+ </button>
112
+ </div>
113
+ </div>
114
+
115
+ {/* Mobile menu overlay */}
116
+ <AnimatePresence>
117
+ {mobileMenuOpen && (
118
+ <>
119
+ {/* Backdrop */}
120
+ <motion.div
121
+ initial={{ opacity: 0 }}
122
+ animate={{ opacity: 1 }}
123
+ exit={{ opacity: 0 }}
124
+ transition={{ duration: 0.2 }}
125
+ className="fixed inset-0 top-16 bg-black/60 backdrop-blur-sm md:hidden"
126
+ onClick={handleOverlayClick}
127
+ aria-hidden="true"
128
+ />
129
+
130
+ {/* Menu panel */}
131
+ <motion.div
132
+ id="mobile-menu"
133
+ initial={{ opacity: 0, y: -10 }}
134
+ animate={{ opacity: 1, y: 0 }}
135
+ exit={{ opacity: 0, y: -10 }}
136
+ transition={{ duration: 0.2 }}
137
+ className="absolute top-16 left-0 right-0 bg-neutral-900 border-b border-neutral-800 md:hidden"
138
+ >
139
+ <nav className="container mx-auto px-4 py-4" aria-label="Mobile navigation">
140
+ <ul className="space-y-1">
141
+ {NAV_ITEMS.map((item) => (
142
+ <li key={item.href}>
143
+ <Link
144
+ href={item.href}
145
+ onClick={() => setMobileMenuOpen(false)}
146
+ className="flex items-center gap-3 px-4 py-3 text-base text-neutral-300 hover:text-white hover:bg-white/5 rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white/20"
147
+ >
148
+ {item.icon && <item.icon className="w-5 h-5" />}
149
+ {item.label}
150
+ </Link>
151
+ </li>
152
+ ))}
153
+ <li className="pt-3 border-t border-neutral-800 mt-3">
154
+ <a
155
+ href="https://github.com/nirholas/github-to-mcp"
156
+ target="_blank"
157
+ rel="noopener noreferrer"
158
+ onClick={() => setMobileMenuOpen(false)}
159
+ className="flex items-center gap-3 px-4 py-3 text-base text-neutral-300 hover:text-white hover:bg-white/5 rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white/20"
160
+ >
161
+ <svg className="w-5 h-5" viewBox="0 0 24 24" fill="currentColor">
162
+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/>
163
+ </svg>
164
+ <span>GitHub</span>
165
+ </a>
166
+ </li>
167
+ </ul>
168
+ </nav>
169
+ </motion.div>
170
+ </>
171
+ )}
172
+ </AnimatePresence>
173
+ </motion.header>
174
+ )
175
+ }