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,467 @@
1
+ /**
2
+ * @fileoverview Unit tests for plugin system
3
+ */
4
+
5
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
6
+ import {
7
+ PluginManager,
8
+ PluginRegistry,
9
+ ExtractorPlugin,
10
+ PluginRepoContext,
11
+ PluginDetectionResult,
12
+ PluginExtractionResult
13
+ } from '../plugins';
14
+
15
+ // Create a mock plugin factory
16
+ function createMockPlugin(overrides: Partial<ExtractorPlugin> = {}): ExtractorPlugin {
17
+ return {
18
+ metadata: {
19
+ id: 'mock-plugin',
20
+ name: 'Mock Plugin',
21
+ version: '1.0.0',
22
+ description: 'A mock plugin for testing'
23
+ },
24
+ detect: vi.fn().mockResolvedValue({
25
+ shouldProcess: true,
26
+ confidence: 0.8,
27
+ reason: 'Mock detection'
28
+ }),
29
+ extract: vi.fn().mockResolvedValue({
30
+ tools: [
31
+ {
32
+ name: 'mock_tool',
33
+ description: 'A mock tool',
34
+ inputSchema: { type: 'object', properties: {}, required: [] },
35
+ source: { type: 'code', file: 'mock.ts' }
36
+ }
37
+ ],
38
+ sourceFiles: ['mock.ts']
39
+ }),
40
+ ...overrides
41
+ };
42
+ }
43
+
44
+ // Create mock repo context
45
+ function createMockContext(): PluginRepoContext {
46
+ return {
47
+ owner: 'test',
48
+ repo: 'repo',
49
+ url: 'https://github.com/test/repo',
50
+ classification: {
51
+ type: 'library',
52
+ confidence: 0.7,
53
+ indicators: ['test']
54
+ },
55
+ metadata: {
56
+ stars: 100,
57
+ language: 'TypeScript'
58
+ }
59
+ };
60
+ }
61
+
62
+ describe('PluginRegistry', () => {
63
+ let registry: PluginRegistry;
64
+
65
+ beforeEach(() => {
66
+ registry = new PluginRegistry();
67
+ });
68
+
69
+ afterEach(async () => {
70
+ await registry.clear();
71
+ });
72
+
73
+ describe('register', () => {
74
+ it('should register a plugin', () => {
75
+ const plugin = createMockPlugin();
76
+ registry.register(plugin);
77
+
78
+ expect(registry.has('mock-plugin')).toBe(true);
79
+ expect(registry.get('mock-plugin')).toBe(plugin);
80
+ });
81
+
82
+ it('should call onRegister hook', () => {
83
+ const onRegister = vi.fn();
84
+ const plugin = createMockPlugin({
85
+ hooks: { onRegister }
86
+ });
87
+
88
+ registry.register(plugin);
89
+
90
+ expect(onRegister).toHaveBeenCalled();
91
+ });
92
+
93
+ it('should accept configuration', () => {
94
+ const plugin = createMockPlugin();
95
+ const config = { option1: 'value1' };
96
+
97
+ registry.register(plugin, config);
98
+
99
+ expect(registry.getConfig('mock-plugin')).toEqual(config);
100
+ });
101
+ });
102
+
103
+ describe('unregister', () => {
104
+ it('should unregister a plugin', async () => {
105
+ const plugin = createMockPlugin();
106
+ registry.register(plugin);
107
+
108
+ const result = await registry.unregister('mock-plugin');
109
+
110
+ expect(result).toBe(true);
111
+ expect(registry.has('mock-plugin')).toBe(false);
112
+ });
113
+
114
+ it('should call onUnregister hook', async () => {
115
+ const onUnregister = vi.fn();
116
+ const plugin = createMockPlugin({
117
+ hooks: { onUnregister }
118
+ });
119
+
120
+ registry.register(plugin);
121
+ await registry.unregister('mock-plugin');
122
+
123
+ expect(onUnregister).toHaveBeenCalled();
124
+ });
125
+
126
+ it('should return false for non-existent plugin', async () => {
127
+ const result = await registry.unregister('non-existent');
128
+ expect(result).toBe(false);
129
+ });
130
+ });
131
+
132
+ describe('list', () => {
133
+ it('should list all registered plugins', () => {
134
+ const plugin1 = createMockPlugin({ metadata: { id: 'plugin-1', name: 'Plugin 1', version: '1.0.0', description: 'Test' } });
135
+ const plugin2 = createMockPlugin({ metadata: { id: 'plugin-2', name: 'Plugin 2', version: '2.0.0', description: 'Test' } });
136
+
137
+ registry.register(plugin1);
138
+ registry.register(plugin2);
139
+
140
+ const list = registry.list();
141
+
142
+ expect(list.length).toBe(2);
143
+ expect(list.map(p => p.id)).toContain('plugin-1');
144
+ expect(list.map(p => p.id)).toContain('plugin-2');
145
+ });
146
+
147
+ it('should include enabled status', () => {
148
+ const plugin = createMockPlugin();
149
+ registry.register(plugin);
150
+
151
+ const list = registry.list();
152
+
153
+ expect(list[0].enabled).toBe(true);
154
+ });
155
+ });
156
+
157
+ describe('enable/disable', () => {
158
+ it('should disable a plugin', () => {
159
+ const plugin = createMockPlugin();
160
+ registry.register(plugin);
161
+
162
+ registry.disable('mock-plugin');
163
+
164
+ const entry = registry.getEntry('mock-plugin');
165
+ expect(entry?.enabled).toBe(false);
166
+ });
167
+
168
+ it('should enable a disabled plugin', () => {
169
+ const plugin = createMockPlugin();
170
+ registry.register(plugin);
171
+ registry.disable('mock-plugin');
172
+
173
+ registry.enable('mock-plugin');
174
+
175
+ const entry = registry.getEntry('mock-plugin');
176
+ expect(entry?.enabled).toBe(true);
177
+ });
178
+
179
+ it('should return false for non-existent plugin', () => {
180
+ expect(registry.disable('non-existent')).toBe(false);
181
+ expect(registry.enable('non-existent')).toBe(false);
182
+ });
183
+ });
184
+
185
+ describe('getEnabled', () => {
186
+ it('should return only enabled plugins', () => {
187
+ const plugin1 = createMockPlugin({ metadata: { id: 'plugin-1', name: 'Plugin 1', version: '1.0.0', description: 'Test' } });
188
+ const plugin2 = createMockPlugin({ metadata: { id: 'plugin-2', name: 'Plugin 2', version: '2.0.0', description: 'Test' } });
189
+
190
+ registry.register(plugin1);
191
+ registry.register(plugin2);
192
+ registry.disable('plugin-2');
193
+
194
+ const enabled = registry.getEnabled();
195
+
196
+ expect(enabled.length).toBe(1);
197
+ expect(enabled[0].metadata.id).toBe('plugin-1');
198
+ });
199
+ });
200
+
201
+ describe('updateConfig', () => {
202
+ it('should update plugin configuration', () => {
203
+ const plugin = createMockPlugin();
204
+ registry.register(plugin, { option1: 'value1' });
205
+
206
+ registry.updateConfig('mock-plugin', { option2: 'value2' });
207
+
208
+ expect(registry.getConfig('mock-plugin')).toEqual({
209
+ option1: 'value1',
210
+ option2: 'value2'
211
+ });
212
+ });
213
+
214
+ it('should validate config against schema', () => {
215
+ const plugin = createMockPlugin({
216
+ configSchema: [
217
+ { name: 'requiredOption', type: 'string', description: 'Required', required: true }
218
+ ]
219
+ });
220
+ registry.register(plugin);
221
+
222
+ expect(() => registry.updateConfig('mock-plugin', {})).toThrow('Missing required config option');
223
+ });
224
+ });
225
+
226
+ describe('events', () => {
227
+ it('should emit events on registration', () => {
228
+ const handler = vi.fn();
229
+ registry.on(handler);
230
+
231
+ const plugin = createMockPlugin();
232
+ registry.register(plugin);
233
+
234
+ expect(handler).toHaveBeenCalledWith(
235
+ expect.objectContaining({
236
+ type: 'registered',
237
+ pluginId: 'mock-plugin'
238
+ })
239
+ );
240
+ });
241
+
242
+ it('should emit events on unregistration', async () => {
243
+ const plugin = createMockPlugin();
244
+ registry.register(plugin);
245
+
246
+ const handler = vi.fn();
247
+ registry.on(handler);
248
+
249
+ await registry.unregister('mock-plugin');
250
+
251
+ expect(handler).toHaveBeenCalledWith(
252
+ expect.objectContaining({
253
+ type: 'unregistered',
254
+ pluginId: 'mock-plugin'
255
+ })
256
+ );
257
+ });
258
+
259
+ it('should allow unsubscribing', () => {
260
+ const handler = vi.fn();
261
+ const unsubscribe = registry.on(handler);
262
+
263
+ unsubscribe();
264
+
265
+ const plugin = createMockPlugin();
266
+ registry.register(plugin);
267
+
268
+ expect(handler).not.toHaveBeenCalled();
269
+ });
270
+ });
271
+
272
+ describe('stats', () => {
273
+ it('should return registry statistics', () => {
274
+ const plugin1 = createMockPlugin({ metadata: { id: 'plugin-1', name: 'Plugin 1', version: '1.0.0', description: 'Test' } });
275
+ const plugin2 = createMockPlugin({ metadata: { id: 'plugin-2', name: 'Plugin 2', version: '2.0.0', description: 'Test' } });
276
+
277
+ registry.register(plugin1);
278
+ registry.register(plugin2);
279
+ registry.disable('plugin-2');
280
+
281
+ const stats = registry.stats();
282
+
283
+ expect(stats.total).toBe(2);
284
+ expect(stats.enabled).toBe(1);
285
+ expect(stats.disabled).toBe(1);
286
+ expect(stats.bySource.inline).toBe(2);
287
+ });
288
+ });
289
+ });
290
+
291
+ describe('PluginManager', () => {
292
+ let manager: PluginManager;
293
+ let registry: PluginRegistry;
294
+
295
+ beforeEach(() => {
296
+ registry = new PluginRegistry();
297
+ manager = new PluginManager({ verbose: false }, registry);
298
+ });
299
+
300
+ afterEach(async () => {
301
+ await manager.clearPlugins();
302
+ });
303
+
304
+ describe('registerPlugin', () => {
305
+ it('should register a plugin', () => {
306
+ const plugin = createMockPlugin();
307
+ manager.registerPlugin(plugin);
308
+
309
+ const list = manager.listPlugins();
310
+ expect(list.length).toBe(1);
311
+ expect(list[0].id).toBe('mock-plugin');
312
+ });
313
+ });
314
+
315
+ describe('unregisterPlugin', () => {
316
+ it('should unregister a plugin', async () => {
317
+ const plugin = createMockPlugin();
318
+ manager.registerPlugin(plugin);
319
+
320
+ const result = await manager.unregisterPlugin('mock-plugin');
321
+
322
+ expect(result).toBe(true);
323
+ expect(manager.listPlugins().length).toBe(0);
324
+ });
325
+ });
326
+
327
+ describe('detectPlugins', () => {
328
+ it('should detect which plugins should process the repo', async () => {
329
+ const plugin = createMockPlugin();
330
+ manager.registerPlugin(plugin);
331
+
332
+ const context = createMockContext();
333
+ const results = await manager.detectPlugins(context, ['file1.ts', 'file2.ts']);
334
+
335
+ expect(results.get('mock-plugin')).toBeDefined();
336
+ expect(results.get('mock-plugin')?.shouldProcess).toBe(true);
337
+ });
338
+
339
+ it('should handle detection errors gracefully', async () => {
340
+ const plugin = createMockPlugin({
341
+ detect: vi.fn().mockRejectedValue(new Error('Detection failed'))
342
+ });
343
+ manager.registerPlugin(plugin);
344
+
345
+ const context = createMockContext();
346
+ const results = await manager.detectPlugins(context, []);
347
+
348
+ expect(results.get('mock-plugin')?.shouldProcess).toBe(false);
349
+ });
350
+ });
351
+
352
+ describe('extract', () => {
353
+ it('should run extraction with applicable plugins', async () => {
354
+ const plugin = createMockPlugin();
355
+ manager.registerPlugin(plugin);
356
+
357
+ const context = createMockContext();
358
+ const getFile = vi.fn().mockResolvedValue('file content');
359
+
360
+ const { tools, results, errors } = await manager.extract(context, getFile, ['file.ts']);
361
+
362
+ expect(tools.length).toBe(1);
363
+ expect(tools[0].name).toBe('mock_tool');
364
+ expect(results.get('mock-plugin')).toBeDefined();
365
+ expect(errors.size).toBe(0);
366
+ });
367
+
368
+ it('should skip plugins that should not process', async () => {
369
+ const plugin = createMockPlugin({
370
+ detect: vi.fn().mockResolvedValue({
371
+ shouldProcess: false,
372
+ confidence: 0,
373
+ reason: 'Not applicable'
374
+ })
375
+ });
376
+ manager.registerPlugin(plugin);
377
+
378
+ const context = createMockContext();
379
+ const getFile = vi.fn();
380
+
381
+ const { tools } = await manager.extract(context, getFile, []);
382
+
383
+ expect(tools.length).toBe(0);
384
+ expect(plugin.extract).not.toHaveBeenCalled();
385
+ });
386
+
387
+ it('should handle extraction errors', async () => {
388
+ const plugin = createMockPlugin({
389
+ extract: vi.fn().mockRejectedValue(new Error('Extraction failed'))
390
+ });
391
+ manager.registerPlugin(plugin);
392
+
393
+ const context = createMockContext();
394
+ const getFile = vi.fn();
395
+
396
+ const { errors } = await manager.extract(context, getFile, []);
397
+
398
+ expect(errors.get('mock-plugin')).toBeDefined();
399
+ expect(errors.get('mock-plugin')?.message).toBe('Extraction failed');
400
+ });
401
+
402
+ it('should call lifecycle hooks', async () => {
403
+ const beforeExtract = vi.fn();
404
+ const afterExtract = vi.fn();
405
+ const plugin = createMockPlugin({
406
+ hooks: { beforeExtract, afterExtract }
407
+ });
408
+ manager.registerPlugin(plugin);
409
+
410
+ const context = createMockContext();
411
+ const getFile = vi.fn();
412
+
413
+ await manager.extract(context, getFile, []);
414
+
415
+ expect(beforeExtract).toHaveBeenCalledWith(context);
416
+ expect(afterExtract).toHaveBeenCalled();
417
+ });
418
+ });
419
+
420
+ describe('extractWithPlugin', () => {
421
+ it('should run extraction with a specific plugin', async () => {
422
+ const plugin = createMockPlugin();
423
+ manager.registerPlugin(plugin);
424
+
425
+ const context = createMockContext();
426
+ const getFile = vi.fn();
427
+
428
+ const result = await manager.extractWithPlugin('mock-plugin', context, getFile);
429
+
430
+ expect(result.tools.length).toBe(1);
431
+ });
432
+
433
+ it('should throw for non-existent plugin', async () => {
434
+ const context = createMockContext();
435
+ const getFile = vi.fn();
436
+
437
+ await expect(
438
+ manager.extractWithPlugin('non-existent', context, getFile)
439
+ ).rejects.toThrow('Plugin not found');
440
+ });
441
+ });
442
+
443
+ describe('enablePlugin/disablePlugin', () => {
444
+ it('should enable and disable plugins', () => {
445
+ const plugin = createMockPlugin();
446
+ manager.registerPlugin(plugin);
447
+
448
+ manager.disablePlugin('mock-plugin');
449
+ expect(manager.listPlugins()[0].enabled).toBe(false);
450
+
451
+ manager.enablePlugin('mock-plugin');
452
+ expect(manager.listPlugins()[0].enabled).toBe(true);
453
+ });
454
+ });
455
+
456
+ describe('getStats', () => {
457
+ it('should return statistics', () => {
458
+ const plugin = createMockPlugin();
459
+ manager.registerPlugin(plugin);
460
+
461
+ const stats = manager.getStats();
462
+
463
+ expect(stats.total).toBe(1);
464
+ expect(stats.enabled).toBe(1);
465
+ });
466
+ });
467
+ });
@@ -0,0 +1,258 @@
1
+ /**
2
+ * @fileoverview Unit tests for readme-extractor module
3
+ */
4
+
5
+ import { describe, it, expect, beforeEach } from 'vitest';
6
+ import { ReadmeExtractor } from '../readme-extractor';
7
+
8
+ describe('ReadmeExtractor', () => {
9
+ let extractor: ReadmeExtractor;
10
+
11
+ beforeEach(() => {
12
+ extractor = new ReadmeExtractor();
13
+ });
14
+
15
+ describe('extract', () => {
16
+ it('should extract tools from JavaScript code examples', async () => {
17
+ const readme = `
18
+ # API Client
19
+
20
+ ## Usage
21
+
22
+ \`\`\`javascript
23
+ const result = await client.get({ id: 123, name: 'test' });
24
+ \`\`\`
25
+ `;
26
+
27
+ const tools = await extractor.extract(readme);
28
+
29
+ expect(tools.length).toBeGreaterThan(0);
30
+ expect(tools[0].source.type).toBe('readme');
31
+ });
32
+
33
+ it('should extract Python MCP tools from code blocks', async () => {
34
+ const readme = `
35
+ # MCP Server
36
+
37
+ \`\`\`python
38
+ @mcp.tool(name="GetWeather", description="Get weather for a location")
39
+ async def get_weather(city: str):
40
+ pass
41
+ \`\`\`
42
+ `;
43
+
44
+ const tools = await extractor.extract(readme);
45
+
46
+ expect(tools.length).toBeGreaterThan(0);
47
+ const weatherTool = tools.find(t => t.name === 'GetWeather');
48
+ expect(weatherTool).toBeDefined();
49
+ expect(weatherTool?.description).toBe('Get weather for a location');
50
+ });
51
+
52
+ it('should extract tools from markdown lists in Tools section', async () => {
53
+ const readme = `
54
+ # My Tool
55
+
56
+ ## Available Tools
57
+
58
+ - **Edit**: Edit files in the repository
59
+ - **View**: View file contents
60
+ - **Search**: Search for patterns in files
61
+ `;
62
+
63
+ const tools = await extractor.extract(readme);
64
+
65
+ expect(tools.length).toBeGreaterThan(0);
66
+ const editTool = tools.find(t => t.name === 'Edit');
67
+ expect(editTool).toBeDefined();
68
+ expect(editTool?.description).toContain('Edit');
69
+ });
70
+
71
+ it('should handle empty README', async () => {
72
+ const tools = await extractor.extract('');
73
+ expect(tools).toEqual([]);
74
+ });
75
+
76
+ it('should handle README with no code examples', async () => {
77
+ const readme = `
78
+ # Project Title
79
+
80
+ This is a project description.
81
+
82
+ ## Installation
83
+
84
+ Run \`npm install\`.
85
+ `;
86
+
87
+ const tools = await extractor.extract(readme);
88
+ // May return empty or minimal tools
89
+ expect(Array.isArray(tools)).toBe(true);
90
+ });
91
+
92
+ it('should extract from TypeScript code blocks', async () => {
93
+ const readme = `
94
+ ## Examples
95
+
96
+ \`\`\`typescript
97
+ const response = await api.create({ name: 'New Item', value: 42 });
98
+ \`\`\`
99
+ `;
100
+
101
+ const tools = await extractor.extract(readme);
102
+
103
+ expect(tools.length).toBeGreaterThan(0);
104
+ });
105
+
106
+ it('should extract multiple API calls from same block', async () => {
107
+ const readme = `
108
+ ## API
109
+
110
+ \`\`\`javascript
111
+ // Get user
112
+ await client.get({ userId: 1 });
113
+
114
+ // Create user
115
+ await client.post({ name: 'John', email: 'john@example.com' });
116
+ \`\`\`
117
+ `;
118
+
119
+ const tools = await extractor.extract(readme);
120
+
121
+ // Should extract at least the first API call
122
+ expect(tools.length).toBeGreaterThan(0);
123
+ });
124
+
125
+ it('should parse parameters from object literals', async () => {
126
+ const readme = `
127
+ \`\`\`javascript
128
+ await client.fetch({
129
+ query: 'search term',
130
+ limit: 10,
131
+ includeMetadata: true
132
+ });
133
+ \`\`\`
134
+ `;
135
+
136
+ const tools = await extractor.extract(readme);
137
+
138
+ if (tools.length > 0) {
139
+ const { properties } = tools[0].inputSchema;
140
+ expect(Object.keys(properties).length).toBeGreaterThan(0);
141
+ }
142
+ });
143
+
144
+ it('should extract from Features section', async () => {
145
+ const readme = `
146
+ # Tool
147
+
148
+ ## Features
149
+
150
+ - **ListFiles**: List all files in a directory
151
+ - **ReadFile**: Read contents of a file
152
+ - **WriteFile**: Write content to a file
153
+ `;
154
+
155
+ const tools = await extractor.extract(readme);
156
+
157
+ // Features section should also be recognized
158
+ expect(Array.isArray(tools)).toBe(true);
159
+ });
160
+
161
+ it('should handle FastMCP decorator patterns', async () => {
162
+ const readme = `
163
+ \`\`\`python
164
+ @server.tool(name="Calculate", description="Perform calculation")
165
+ async def calculate(expression: str):
166
+ return eval(expression)
167
+ \`\`\`
168
+ `;
169
+
170
+ const tools = await extractor.extract(readme);
171
+
172
+ // Should recognize @server.tool pattern
173
+ expect(Array.isArray(tools)).toBe(true);
174
+ });
175
+ });
176
+
177
+ describe('extractDocumentationLinks', () => {
178
+ it('should extract documentation URLs', () => {
179
+ const readme = `
180
+ # Project
181
+
182
+ Check out [API Docs](https://docs.example.com/api) for more info.
183
+ See the [Developer Guide](https://developer.example.com/guide).
184
+ `;
185
+
186
+ const links = extractor.extractDocumentationLinks(readme);
187
+
188
+ expect(links.length).toBe(2);
189
+ expect(links[0].url).toBe('https://docs.example.com/api');
190
+ expect(links[1].url).toBe('https://developer.example.com/guide');
191
+ });
192
+
193
+ it('should filter non-documentation links', () => {
194
+ const readme = `
195
+ Check [GitHub](https://github.com/owner/repo) repo.
196
+ Read [Documentation](https://docs.example.com/).
197
+ `;
198
+
199
+ const links = extractor.extractDocumentationLinks(readme);
200
+
201
+ // Should only include docs link
202
+ const docsLinks = links.filter(l => l.url.includes('docs'));
203
+ expect(docsLinks.length).toBe(1);
204
+ });
205
+
206
+ it('should return empty array for no links', () => {
207
+ const readme = 'No links here.';
208
+ const links = extractor.extractDocumentationLinks(readme);
209
+ expect(links).toEqual([]);
210
+ });
211
+ });
212
+
213
+ describe('edge cases', () => {
214
+ it('should handle malformed markdown gracefully', async () => {
215
+ const readme = `
216
+ \`\`\`
217
+ unclosed code block
218
+ `;
219
+
220
+ // Should not throw
221
+ const tools = await extractor.extract(readme);
222
+ expect(Array.isArray(tools)).toBe(true);
223
+ });
224
+
225
+ it('should handle very long README files', async () => {
226
+ const longContent = '# Title\n\n' + 'Lorem ipsum. '.repeat(10000);
227
+
228
+ const tools = await extractor.extract(longContent);
229
+ expect(Array.isArray(tools)).toBe(true);
230
+ });
231
+
232
+ it('should handle README with only headers', async () => {
233
+ const readme = `
234
+ # Title
235
+ ## Section 1
236
+ ### Subsection
237
+ ## Section 2
238
+ `;
239
+
240
+ const tools = await extractor.extract(readme);
241
+ expect(Array.isArray(tools)).toBe(true);
242
+ });
243
+
244
+ it('should extract tools with code formatting in names', async () => {
245
+ const readme = `
246
+ ## Tools
247
+
248
+ - \`GrepTool\`: Search files using regex
249
+ - \`GlobTool\`: Find files by pattern
250
+ `;
251
+
252
+ const tools = await extractor.extract(readme);
253
+
254
+ // Should handle backtick-formatted names
255
+ expect(Array.isArray(tools)).toBe(true);
256
+ });
257
+ });
258
+ });