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,1114 @@
1
+ /**
2
+ * @fileoverview Multi-language documentation parsers
3
+ * @copyright Copyright (c) 2024-2026 nirholas
4
+ * @license MIT
5
+ */
6
+
7
+ import type { ParsedDocumentation, ExtractedTool, ConfidenceFactors } from './types';
8
+
9
+ /**
10
+ * Base class for language-specific documentation parsers
11
+ */
12
+ abstract class BaseDocParser {
13
+ abstract parseDocumentation(code: string, position: number): ParsedDocumentation | null;
14
+ abstract extractFunctions(code: string, filename: string): ExtractedTool[];
15
+
16
+ /**
17
+ * Calculate confidence score based on documentation quality
18
+ */
19
+ protected calculateConfidence(
20
+ doc: ParsedDocumentation | null,
21
+ hasTypeInfo: boolean,
22
+ sourceType: string
23
+ ): { confidence: number; factors: ConfidenceFactors } {
24
+ const factors: ConfidenceFactors = {
25
+ documentation: 0,
26
+ types: 0,
27
+ examples: 0,
28
+ source: 0
29
+ };
30
+
31
+ // Documentation factor
32
+ if (doc) {
33
+ let docScore = 0;
34
+ if (doc.description && doc.description.length > 10) docScore += 0.4;
35
+ if (doc.params.length > 0) {
36
+ const paramsWithDesc = doc.params.filter(p => p.description).length;
37
+ docScore += 0.3 * (paramsWithDesc / doc.params.length);
38
+ }
39
+ if (doc.returns?.description) docScore += 0.2;
40
+ if (doc.params.every(p => p.type)) docScore += 0.1;
41
+ factors.documentation = Math.min(1, docScore);
42
+ }
43
+
44
+ // Types factor
45
+ factors.types = hasTypeInfo ? 0.8 : 0.2;
46
+ if (doc?.params.every(p => p.type)) factors.types = 1;
47
+
48
+ // Examples factor
49
+ factors.examples = doc?.examples && doc.examples.length > 0 ? 1 : 0;
50
+
51
+ // Source reliability factor
52
+ const sourceScores: Record<string, number> = {
53
+ 'mcp-introspect': 1.0,
54
+ 'openapi': 0.95,
55
+ 'graphql': 0.9,
56
+ 'code': 0.7,
57
+ 'tests': 0.6,
58
+ 'docs': 0.5,
59
+ 'examples': 0.5,
60
+ 'readme': 0.4,
61
+ 'universal': 0.3
62
+ };
63
+ factors.source = sourceScores[sourceType] || 0.5;
64
+
65
+ // Calculate overall confidence
66
+ const confidence = (
67
+ factors.documentation * 0.35 +
68
+ factors.types * 0.25 +
69
+ factors.examples * 0.15 +
70
+ factors.source * 0.25
71
+ );
72
+
73
+ return { confidence: Math.round(confidence * 100) / 100, factors };
74
+ }
75
+
76
+ /**
77
+ * Convert type string to JSON Schema type
78
+ */
79
+ protected toJsonSchemaType(type: string): string {
80
+ const normalized = type.toLowerCase().trim();
81
+
82
+ if (normalized.includes('str') || normalized === 'string') return 'string';
83
+ if (normalized.includes('int') || normalized.includes('number') || normalized.includes('float')) return 'number';
84
+ if (normalized.includes('bool')) return 'boolean';
85
+ if (normalized.includes('list') || normalized.includes('array') || normalized.includes('[]')) return 'array';
86
+ if (normalized.includes('dict') || normalized.includes('object') || normalized.includes('map') || normalized.includes('hash')) return 'object';
87
+ if (normalized === 'null' || normalized === 'nil' || normalized === 'none') return 'null';
88
+
89
+ return 'string';
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Python docstring parser supporting Google, NumPy, and Sphinx styles
95
+ */
96
+ export class PythonDocParser extends BaseDocParser {
97
+ /**
98
+ * Parse Python docstring at a given position
99
+ */
100
+ parseDocumentation(code: string, position: number): ParsedDocumentation | null {
101
+ // Find docstring after function definition
102
+ const codeFromPos = code.substring(position);
103
+
104
+ // Match triple-quoted docstrings
105
+ const docMatch = codeFromPos.match(/^[^"']*(?:"""([\s\S]*?)"""|'''([\s\S]*?)''')/);
106
+ if (!docMatch) return null;
107
+
108
+ const docstring = (docMatch[1] || docMatch[2]).trim();
109
+ return this.parseDocstring(docstring);
110
+ }
111
+
112
+ /**
113
+ * Parse a Python docstring in various formats
114
+ */
115
+ private parseDocstring(docstring: string): ParsedDocumentation {
116
+ const result: ParsedDocumentation = {
117
+ params: [],
118
+ examples: []
119
+ };
120
+
121
+ const lines = docstring.split('\n');
122
+ let currentSection = 'description';
123
+ let descriptionLines: string[] = [];
124
+ let currentParam: { name: string; type?: string; description: string } | null = null;
125
+
126
+ for (let i = 0; i < lines.length; i++) {
127
+ const line = lines[i];
128
+ const trimmed = line.trim();
129
+
130
+ // Detect section headers (Google/NumPy style)
131
+ if (/^(Args|Arguments|Parameters):?\s*$/i.test(trimmed)) {
132
+ currentSection = 'params';
133
+ continue;
134
+ }
135
+ if (/^(Returns?|Yields?):?\s*$/i.test(trimmed)) {
136
+ currentSection = 'returns';
137
+ continue;
138
+ }
139
+ if (/^(Examples?):?\s*$/i.test(trimmed)) {
140
+ currentSection = 'examples';
141
+ continue;
142
+ }
143
+ if (/^(Raises?|Throws?|Exceptions?):?\s*$/i.test(trimmed)) {
144
+ currentSection = 'raises';
145
+ continue;
146
+ }
147
+
148
+ // Parse based on current section
149
+ if (currentSection === 'description') {
150
+ // Check for Sphinx-style :param:
151
+ const sphinxParam = trimmed.match(/^:param\s+(\w+):\s*(.+)$/);
152
+ if (sphinxParam) {
153
+ result.params.push({
154
+ name: sphinxParam[1],
155
+ description: sphinxParam[2],
156
+ required: true
157
+ });
158
+ continue;
159
+ }
160
+
161
+ const sphinxType = trimmed.match(/^:type\s+(\w+):\s*(.+)$/);
162
+ if (sphinxType) {
163
+ const param = result.params.find(p => p.name === sphinxType[1]);
164
+ if (param) param.type = sphinxType[2];
165
+ continue;
166
+ }
167
+
168
+ const sphinxReturn = trimmed.match(/^:returns?:\s*(.+)$/);
169
+ if (sphinxReturn) {
170
+ result.returns = { description: sphinxReturn[1] };
171
+ continue;
172
+ }
173
+
174
+ const sphinxRtype = trimmed.match(/^:rtype:\s*(.+)$/);
175
+ if (sphinxRtype) {
176
+ if (!result.returns) result.returns = {};
177
+ result.returns.type = sphinxRtype[1];
178
+ continue;
179
+ }
180
+
181
+ descriptionLines.push(trimmed);
182
+ } else if (currentSection === 'params') {
183
+ // Google style: name (type): description
184
+ // NumPy style: name : type\n description
185
+ const googleMatch = trimmed.match(/^(\w+)\s*\(([^)]+)\)\s*:\s*(.*)$/);
186
+ if (googleMatch) {
187
+ if (currentParam) result.params.push(currentParam);
188
+ currentParam = {
189
+ name: googleMatch[1],
190
+ type: googleMatch[2],
191
+ description: googleMatch[3]
192
+ };
193
+ continue;
194
+ }
195
+
196
+ const simpleMatch = trimmed.match(/^(\w+)\s*:\s*(.+)$/);
197
+ if (simpleMatch) {
198
+ if (currentParam) result.params.push(currentParam);
199
+ // Check if it's type or description
200
+ const isType = /^[A-Za-z\[\]|,\s]+$/.test(simpleMatch[2]);
201
+ currentParam = {
202
+ name: simpleMatch[1],
203
+ type: isType ? simpleMatch[2] : undefined,
204
+ description: isType ? '' : simpleMatch[2]
205
+ };
206
+ continue;
207
+ }
208
+
209
+ // Continuation line
210
+ if (currentParam && trimmed && line.startsWith(' ')) {
211
+ currentParam.description += ' ' + trimmed;
212
+ }
213
+ } else if (currentSection === 'returns') {
214
+ if (trimmed) {
215
+ const typeMatch = trimmed.match(/^([A-Za-z\[\]|,\s]+):\s*(.*)$/);
216
+ if (typeMatch) {
217
+ result.returns = {
218
+ type: typeMatch[1],
219
+ description: typeMatch[2]
220
+ };
221
+ } else {
222
+ result.returns = { description: trimmed };
223
+ }
224
+ }
225
+ } else if (currentSection === 'examples') {
226
+ if (trimmed.startsWith('>>>') || trimmed.startsWith('```')) {
227
+ result.examples!.push(trimmed);
228
+ }
229
+ } else if (currentSection === 'raises') {
230
+ if (!result.throws) result.throws = [];
231
+ if (trimmed) result.throws.push(trimmed);
232
+ }
233
+ }
234
+
235
+ if (currentParam) result.params.push(currentParam);
236
+
237
+ // Clean up description
238
+ result.description = descriptionLines
239
+ .filter(l => l.length > 0)
240
+ .join(' ')
241
+ .trim();
242
+
243
+ // Mark params as required/optional
244
+ result.params = result.params.map(p => ({
245
+ ...p,
246
+ required: !p.type?.toLowerCase().includes('optional') && p.defaultValue === undefined
247
+ }));
248
+
249
+ return result;
250
+ }
251
+
252
+ /**
253
+ * Extract function definitions with docstrings from Python code
254
+ */
255
+ extractFunctions(code: string, filename: string): ExtractedTool[] {
256
+ const tools: ExtractedTool[] = [];
257
+
258
+ // Match function definitions with optional async and decorators
259
+ const funcPattern = /(?:@[\w.]+\s*(?:\([^)]*\))?\s*\n\s*)*(?:async\s+)?def\s+(\w+)\s*\(([^)]*)\)(?:\s*->\s*([^:]+))?\s*:/g;
260
+
261
+ let match;
262
+ while ((match = funcPattern.exec(code)) !== null) {
263
+ const [fullMatch, funcName, paramsStr, returnType] = match;
264
+
265
+ // Skip private/dunder methods
266
+ if (funcName.startsWith('_') && !funcName.startsWith('__')) continue;
267
+ if (funcName === '__init__') continue;
268
+
269
+ const doc = this.parseDocumentation(code, match.index + fullMatch.length);
270
+ const params = this.parseParameters(paramsStr, doc);
271
+ const hasTypeInfo = returnType !== undefined || params.some(p => p.type !== undefined);
272
+
273
+ const { confidence, factors } = this.calculateConfidence(doc, hasTypeInfo, 'code');
274
+
275
+ const lineNum = code.substring(0, match.index).split('\n').length;
276
+
277
+ tools.push({
278
+ name: funcName,
279
+ description: doc?.description || `Python function: ${funcName}`,
280
+ inputSchema: {
281
+ type: 'object',
282
+ properties: this.paramsToProperties(params),
283
+ required: params.filter(p => p.required).map(p => p.name)
284
+ },
285
+ examples: doc?.examples,
286
+ source: {
287
+ type: 'code',
288
+ file: filename,
289
+ line: lineNum
290
+ },
291
+ confidence,
292
+ confidenceFactors: factors
293
+ });
294
+ }
295
+
296
+ return tools;
297
+ }
298
+
299
+ /**
300
+ * Parse function parameters from signature
301
+ */
302
+ private parseParameters(paramsStr: string, doc: ParsedDocumentation | null): Array<{
303
+ name: string;
304
+ type?: string;
305
+ description?: string;
306
+ required: boolean;
307
+ defaultValue?: any;
308
+ }> {
309
+ const params: Array<{
310
+ name: string;
311
+ type?: string;
312
+ description?: string;
313
+ required: boolean;
314
+ defaultValue?: any;
315
+ }> = [];
316
+
317
+ // Skip empty or self-only
318
+ if (!paramsStr.trim() || paramsStr.trim() === 'self') return params;
319
+
320
+ // Split handling nested brackets
321
+ const paramParts = this.splitParams(paramsStr);
322
+
323
+ for (const part of paramParts) {
324
+ const trimmed = part.trim();
325
+ if (!trimmed || trimmed === 'self' || trimmed === 'cls') continue;
326
+ if (trimmed.startsWith('*')) continue; // Skip *args, **kwargs
327
+
328
+ // Parse: name: Type = default
329
+ const paramMatch = trimmed.match(/^(\w+)(?:\s*:\s*([^=]+))?(?:\s*=\s*(.+))?$/);
330
+ if (paramMatch) {
331
+ const [, name, type, defaultVal] = paramMatch;
332
+ const docParam = doc?.params.find(p => p.name === name);
333
+
334
+ params.push({
335
+ name,
336
+ type: type?.trim() || docParam?.type,
337
+ description: docParam?.description,
338
+ required: defaultVal === undefined && !type?.toLowerCase().includes('optional'),
339
+ defaultValue: defaultVal !== undefined ? this.parseDefaultValue(defaultVal) : undefined
340
+ });
341
+ }
342
+ }
343
+
344
+ return params;
345
+ }
346
+
347
+ /**
348
+ * Split parameters handling nested brackets
349
+ */
350
+ private splitParams(paramsStr: string): string[] {
351
+ const params: string[] = [];
352
+ let depth = 0;
353
+ let current = '';
354
+
355
+ for (const char of paramsStr) {
356
+ if (char === '(' || char === '[' || char === '{') depth++;
357
+ if (char === ')' || char === ']' || char === '}') depth--;
358
+ if (char === ',' && depth === 0) {
359
+ params.push(current);
360
+ current = '';
361
+ } else {
362
+ current += char;
363
+ }
364
+ }
365
+ if (current.trim()) params.push(current);
366
+
367
+ return params;
368
+ }
369
+
370
+ /**
371
+ * Parse Python default value
372
+ */
373
+ private parseDefaultValue(value: string): any {
374
+ const v = value.trim();
375
+ if (v === 'None') return null;
376
+ if (v === 'True') return true;
377
+ if (v === 'False') return false;
378
+ if (/^["']/.test(v)) return v.slice(1, -1);
379
+ if (/^\d+$/.test(v)) return parseInt(v);
380
+ if (/^\d+\.\d+$/.test(v)) return parseFloat(v);
381
+ if (v === '[]') return [];
382
+ if (v === '{}') return {};
383
+ return v;
384
+ }
385
+
386
+ /**
387
+ * Convert params to JSON Schema properties
388
+ */
389
+ private paramsToProperties(params: Array<{
390
+ name: string;
391
+ type?: string;
392
+ description?: string;
393
+ }>): Record<string, any> {
394
+ const props: Record<string, any> = {};
395
+
396
+ for (const param of params) {
397
+ props[param.name] = {
398
+ type: param.type ? this.toJsonSchemaType(param.type) : 'string',
399
+ description: param.description || `${param.name} parameter`
400
+ };
401
+ }
402
+
403
+ return props;
404
+ }
405
+ }
406
+
407
+ /**
408
+ * Rust doc comment parser
409
+ */
410
+ export class RustDocParser extends BaseDocParser {
411
+ parseDocumentation(code: string, position: number): ParsedDocumentation | null {
412
+ const codeBeforePos = code.substring(0, position);
413
+ const lines = codeBeforePos.split('\n');
414
+
415
+ const docLines: string[] = [];
416
+
417
+ // Walk backwards to collect doc comments
418
+ for (let i = lines.length - 1; i >= 0; i--) {
419
+ const line = lines[i].trim();
420
+ if (line.startsWith('///')) {
421
+ docLines.unshift(line.substring(3).trim());
422
+ } else if (line.startsWith('#[doc = "')) {
423
+ const match = line.match(/#\[doc = "(.+)"\]/);
424
+ if (match) docLines.unshift(match[1]);
425
+ } else if (line === '' || line.startsWith('#[')) {
426
+ continue;
427
+ } else {
428
+ break;
429
+ }
430
+ }
431
+
432
+ if (docLines.length === 0) return null;
433
+
434
+ return this.parseRustDoc(docLines);
435
+ }
436
+
437
+ private parseRustDoc(lines: string[]): ParsedDocumentation {
438
+ const result: ParsedDocumentation = {
439
+ params: [],
440
+ examples: []
441
+ };
442
+
443
+ let currentSection = 'description';
444
+ const descLines: string[] = [];
445
+
446
+ for (const line of lines) {
447
+ if (line.startsWith('# Arguments') || line.startsWith('# Parameters')) {
448
+ currentSection = 'params';
449
+ continue;
450
+ }
451
+ if (line.startsWith('# Returns')) {
452
+ currentSection = 'returns';
453
+ continue;
454
+ }
455
+ if (line.startsWith('# Examples') || line.startsWith('# Example')) {
456
+ currentSection = 'examples';
457
+ continue;
458
+ }
459
+ if (line.startsWith('# Panics') || line.startsWith('# Errors')) {
460
+ currentSection = 'errors';
461
+ continue;
462
+ }
463
+
464
+ if (currentSection === 'description') {
465
+ descLines.push(line);
466
+ } else if (currentSection === 'params') {
467
+ // Format: * `name` - description
468
+ const paramMatch = line.match(/^\*\s*`(\w+)`\s*-\s*(.+)$/);
469
+ if (paramMatch) {
470
+ result.params.push({
471
+ name: paramMatch[1],
472
+ description: paramMatch[2],
473
+ required: true
474
+ });
475
+ }
476
+ } else if (currentSection === 'returns') {
477
+ result.returns = { description: line };
478
+ } else if (currentSection === 'examples') {
479
+ result.examples!.push(line);
480
+ }
481
+ }
482
+
483
+ result.description = descLines.join(' ').trim();
484
+
485
+ return result;
486
+ }
487
+
488
+ extractFunctions(code: string, filename: string): ExtractedTool[] {
489
+ const tools: ExtractedTool[] = [];
490
+
491
+ // Match pub fn definitions
492
+ const funcPattern = /(?:pub\s+)?(?:async\s+)?fn\s+(\w+)(?:<[^>]+>)?\s*\(([^)]*)\)(?:\s*->\s*([^{]+))?\s*\{/g;
493
+
494
+ let match;
495
+ while ((match = funcPattern.exec(code)) !== null) {
496
+ const [fullMatch, funcName, paramsStr, returnType] = match;
497
+
498
+ // Skip private functions
499
+ if (funcName.startsWith('_')) continue;
500
+
501
+ const doc = this.parseDocumentation(code, match.index);
502
+ const params = this.parseRustParams(paramsStr, doc);
503
+ const hasTypeInfo = returnType !== undefined || params.some(p => p.type !== undefined);
504
+
505
+ const { confidence, factors } = this.calculateConfidence(doc, hasTypeInfo, 'code');
506
+ const lineNum = code.substring(0, match.index).split('\n').length;
507
+
508
+ tools.push({
509
+ name: funcName,
510
+ description: doc?.description || `Rust function: ${funcName}`,
511
+ inputSchema: {
512
+ type: 'object',
513
+ properties: this.rustParamsToProperties(params),
514
+ required: params.filter(p => !p.type?.includes('Option')).map(p => p.name)
515
+ },
516
+ source: {
517
+ type: 'code',
518
+ file: filename,
519
+ line: lineNum
520
+ },
521
+ confidence,
522
+ confidenceFactors: factors
523
+ });
524
+ }
525
+
526
+ return tools;
527
+ }
528
+
529
+ private parseRustParams(paramsStr: string, doc: ParsedDocumentation | null): Array<{
530
+ name: string;
531
+ type?: string;
532
+ description?: string;
533
+ }> {
534
+ const params: Array<{ name: string; type?: string; description?: string }> = [];
535
+
536
+ const parts = paramsStr.split(',');
537
+ for (const part of parts) {
538
+ const trimmed = part.trim();
539
+ if (!trimmed || trimmed === 'self' || trimmed === '&self' || trimmed === '&mut self') continue;
540
+
541
+ // Format: name: Type
542
+ const match = trimmed.match(/(\w+)\s*:\s*(.+)/);
543
+ if (match) {
544
+ const docParam = doc?.params.find(p => p.name === match[1]);
545
+ params.push({
546
+ name: match[1],
547
+ type: match[2].trim(),
548
+ description: docParam?.description
549
+ });
550
+ }
551
+ }
552
+
553
+ return params;
554
+ }
555
+
556
+ private rustParamsToProperties(params: Array<{ name: string; type?: string; description?: string }>): Record<string, any> {
557
+ const props: Record<string, any> = {};
558
+
559
+ for (const param of params) {
560
+ props[param.name] = {
561
+ type: this.rustTypeToJson(param.type || ''),
562
+ description: param.description || `${param.name} parameter`
563
+ };
564
+ }
565
+
566
+ return props;
567
+ }
568
+
569
+ private rustTypeToJson(rustType: string): string {
570
+ const t = rustType.toLowerCase();
571
+ if (t.includes('string') || t.includes('str')) return 'string';
572
+ if (t.includes('i32') || t.includes('i64') || t.includes('u32') || t.includes('u64') || t.includes('usize') || t.includes('isize')) return 'integer';
573
+ if (t.includes('f32') || t.includes('f64')) return 'number';
574
+ if (t.includes('bool')) return 'boolean';
575
+ if (t.includes('vec') || t.includes('array')) return 'array';
576
+ if (t.includes('hashmap') || t.includes('btreemap')) return 'object';
577
+ return 'string';
578
+ }
579
+ }
580
+
581
+ /**
582
+ * Go godoc comment parser
583
+ */
584
+ export class GoDocParser extends BaseDocParser {
585
+ parseDocumentation(code: string, position: number): ParsedDocumentation | null {
586
+ const codeBeforePos = code.substring(0, position);
587
+ const lines = codeBeforePos.split('\n');
588
+
589
+ const docLines: string[] = [];
590
+
591
+ // Walk backwards to collect comments
592
+ for (let i = lines.length - 1; i >= 0; i--) {
593
+ const line = lines[i].trim();
594
+ if (line.startsWith('//')) {
595
+ docLines.unshift(line.substring(2).trim());
596
+ } else if (line === '') {
597
+ continue;
598
+ } else {
599
+ break;
600
+ }
601
+ }
602
+
603
+ if (docLines.length === 0) return null;
604
+
605
+ return {
606
+ description: docLines.join(' ').trim(),
607
+ params: []
608
+ };
609
+ }
610
+
611
+ extractFunctions(code: string, filename: string): ExtractedTool[] {
612
+ const tools: ExtractedTool[] = [];
613
+
614
+ // Match exported function definitions (capitalized)
615
+ const funcPattern = /func\s+(?:\([^)]+\)\s+)?([A-Z]\w*)\s*\(([^)]*)\)(?:\s*(?:\([^)]*\)|[^{]+))?\s*\{/g;
616
+
617
+ let match;
618
+ while ((match = funcPattern.exec(code)) !== null) {
619
+ const [, funcName, paramsStr] = match;
620
+
621
+ const doc = this.parseDocumentation(code, match.index);
622
+ const params = this.parseGoParams(paramsStr);
623
+
624
+ const { confidence, factors } = this.calculateConfidence(doc, params.some(p => p.type !== undefined), 'code');
625
+ const lineNum = code.substring(0, match.index).split('\n').length;
626
+
627
+ tools.push({
628
+ name: funcName,
629
+ description: doc?.description || `Go function: ${funcName}`,
630
+ inputSchema: {
631
+ type: 'object',
632
+ properties: this.goParamsToProperties(params),
633
+ required: params.map(p => p.name)
634
+ },
635
+ source: {
636
+ type: 'code',
637
+ file: filename,
638
+ line: lineNum
639
+ },
640
+ confidence,
641
+ confidenceFactors: factors
642
+ });
643
+ }
644
+
645
+ return tools;
646
+ }
647
+
648
+ private parseGoParams(paramsStr: string): Array<{ name: string; type?: string }> {
649
+ const params: Array<{ name: string; type?: string }> = [];
650
+
651
+ // Go params: name type, name type or name, name type
652
+ const parts = paramsStr.split(',');
653
+ let pendingType: string | undefined;
654
+ const pendingNames: string[] = [];
655
+
656
+ for (const part of parts) {
657
+ const trimmed = part.trim();
658
+ if (!trimmed) continue;
659
+
660
+ const tokens = trimmed.split(/\s+/);
661
+ if (tokens.length >= 2) {
662
+ // Last token is type, rest are names for this type
663
+ const type = tokens.pop()!;
664
+ for (const name of tokens) {
665
+ params.push({ name, type });
666
+ }
667
+ // Also apply to pending names
668
+ for (const name of pendingNames) {
669
+ params.push({ name, type });
670
+ }
671
+ pendingNames.length = 0;
672
+ } else if (tokens.length === 1) {
673
+ // Just a name, type comes later
674
+ pendingNames.push(tokens[0]);
675
+ }
676
+ }
677
+
678
+ return params;
679
+ }
680
+
681
+ private goParamsToProperties(params: Array<{ name: string; type?: string }>): Record<string, any> {
682
+ const props: Record<string, any> = {};
683
+
684
+ for (const param of params) {
685
+ props[param.name] = {
686
+ type: this.goTypeToJson(param.type || ''),
687
+ description: `${param.name} parameter`
688
+ };
689
+ }
690
+
691
+ return props;
692
+ }
693
+
694
+ private goTypeToJson(goType: string): string {
695
+ const t = goType.toLowerCase();
696
+ if (t === 'string') return 'string';
697
+ if (t.includes('int') || t.includes('byte') || t.includes('rune')) return 'integer';
698
+ if (t.includes('float')) return 'number';
699
+ if (t === 'bool') return 'boolean';
700
+ if (t.startsWith('[]') || t.startsWith('...')) return 'array';
701
+ if (t.startsWith('map[')) return 'object';
702
+ return 'string';
703
+ }
704
+ }
705
+
706
+ /**
707
+ * Ruby YARD documentation parser
708
+ */
709
+ export class RubyDocParser extends BaseDocParser {
710
+ parseDocumentation(code: string, position: number): ParsedDocumentation | null {
711
+ const codeBeforePos = code.substring(0, position);
712
+ const lines = codeBeforePos.split('\n');
713
+
714
+ const docLines: string[] = [];
715
+
716
+ // Walk backwards to collect comments
717
+ for (let i = lines.length - 1; i >= 0; i--) {
718
+ const line = lines[i].trim();
719
+ if (line.startsWith('#')) {
720
+ docLines.unshift(line.substring(1).trim());
721
+ } else if (line === '') {
722
+ continue;
723
+ } else {
724
+ break;
725
+ }
726
+ }
727
+
728
+ if (docLines.length === 0) return null;
729
+
730
+ return this.parseYardDoc(docLines);
731
+ }
732
+
733
+ private parseYardDoc(lines: string[]): ParsedDocumentation {
734
+ const result: ParsedDocumentation = {
735
+ params: [],
736
+ examples: []
737
+ };
738
+
739
+ const descLines: string[] = [];
740
+ let inExample = false;
741
+
742
+ for (const line of lines) {
743
+ // YARD tags
744
+ const paramMatch = line.match(/^@param\s+(?:\[([^\]]+)\]\s+)?(\w+)\s+(.*)$/);
745
+ if (paramMatch) {
746
+ result.params.push({
747
+ name: paramMatch[2],
748
+ type: paramMatch[1],
749
+ description: paramMatch[3],
750
+ required: !paramMatch[1]?.includes('nil')
751
+ });
752
+ continue;
753
+ }
754
+
755
+ const returnMatch = line.match(/^@return\s+(?:\[([^\]]+)\]\s+)?(.*)$/);
756
+ if (returnMatch) {
757
+ result.returns = {
758
+ type: returnMatch[1],
759
+ description: returnMatch[2]
760
+ };
761
+ continue;
762
+ }
763
+
764
+ if (line.startsWith('@example')) {
765
+ inExample = true;
766
+ continue;
767
+ }
768
+
769
+ if (line.startsWith('@')) {
770
+ inExample = false;
771
+ continue;
772
+ }
773
+
774
+ if (inExample) {
775
+ result.examples!.push(line);
776
+ } else if (!line.startsWith('@')) {
777
+ descLines.push(line);
778
+ }
779
+ }
780
+
781
+ result.description = descLines.join(' ').trim();
782
+
783
+ return result;
784
+ }
785
+
786
+ extractFunctions(code: string, filename: string): ExtractedTool[] {
787
+ const tools: ExtractedTool[] = [];
788
+
789
+ // Match Ruby method definitions
790
+ const methodPattern = /def\s+(\w+[?!]?)(?:\s*\(([^)]*)\))?/g;
791
+
792
+ let match;
793
+ while ((match = methodPattern.exec(code)) !== null) {
794
+ const [, methodName, paramsStr] = match;
795
+
796
+ // Skip private methods
797
+ if (methodName.startsWith('_')) continue;
798
+
799
+ const doc = this.parseDocumentation(code, match.index);
800
+ const params = this.parseRubyParams(paramsStr || '', doc);
801
+
802
+ const { confidence, factors } = this.calculateConfidence(doc, doc?.params.some(p => p.type !== undefined) || false, 'code');
803
+ const lineNum = code.substring(0, match.index).split('\n').length;
804
+
805
+ tools.push({
806
+ name: methodName,
807
+ description: doc?.description || `Ruby method: ${methodName}`,
808
+ inputSchema: {
809
+ type: 'object',
810
+ properties: this.rubyParamsToProperties(params),
811
+ required: params.filter(p => !p.hasDefault).map(p => p.name)
812
+ },
813
+ source: {
814
+ type: 'code',
815
+ file: filename,
816
+ line: lineNum
817
+ },
818
+ confidence,
819
+ confidenceFactors: factors
820
+ });
821
+ }
822
+
823
+ return tools;
824
+ }
825
+
826
+ private parseRubyParams(paramsStr: string, doc: ParsedDocumentation | null): Array<{
827
+ name: string;
828
+ type?: string;
829
+ hasDefault: boolean;
830
+ }> {
831
+ const params: Array<{ name: string; type?: string; hasDefault: boolean }> = [];
832
+
833
+ const parts = paramsStr.split(',');
834
+ for (const part of parts) {
835
+ const trimmed = part.trim();
836
+ if (!trimmed) continue;
837
+
838
+ // Handle keyword args: name:, name: default
839
+ const keywordMatch = trimmed.match(/^(\w+):\s*(.*)$/);
840
+ if (keywordMatch) {
841
+ const docParam = doc?.params.find(p => p.name === keywordMatch[1]);
842
+ params.push({
843
+ name: keywordMatch[1],
844
+ type: docParam?.type,
845
+ hasDefault: keywordMatch[2] !== ''
846
+ });
847
+ continue;
848
+ }
849
+
850
+ // Handle positional: name, name = default
851
+ const posMatch = trimmed.match(/^(\w+)(?:\s*=\s*.+)?$/);
852
+ if (posMatch) {
853
+ const docParam = doc?.params.find(p => p.name === posMatch[1]);
854
+ params.push({
855
+ name: posMatch[1],
856
+ type: docParam?.type,
857
+ hasDefault: trimmed.includes('=')
858
+ });
859
+ }
860
+ }
861
+
862
+ return params;
863
+ }
864
+
865
+ private rubyParamsToProperties(params: Array<{ name: string; type?: string }>): Record<string, any> {
866
+ const props: Record<string, any> = {};
867
+
868
+ for (const param of params) {
869
+ props[param.name] = {
870
+ type: this.rubyTypeToJson(param.type || ''),
871
+ description: `${param.name} parameter`
872
+ };
873
+ }
874
+
875
+ return props;
876
+ }
877
+
878
+ private rubyTypeToJson(rubyType: string): string {
879
+ const t = rubyType.toLowerCase();
880
+ if (t.includes('string')) return 'string';
881
+ if (t.includes('integer') || t.includes('fixnum')) return 'integer';
882
+ if (t.includes('float') || t.includes('numeric')) return 'number';
883
+ if (t.includes('bool') || t.includes('trueclass') || t.includes('falseclass')) return 'boolean';
884
+ if (t.includes('array')) return 'array';
885
+ if (t.includes('hash')) return 'object';
886
+ return 'string';
887
+ }
888
+ }
889
+
890
+ /**
891
+ * Enhanced TypeScript/JavaScript JSDoc/TSDoc parser
892
+ */
893
+ export class TypeScriptDocParser extends BaseDocParser {
894
+ parseDocumentation(code: string, position: number): ParsedDocumentation | null {
895
+ const codeBeforePos = code.substring(0, position);
896
+
897
+ // Find JSDoc comment before position
898
+ const jsdocMatch = codeBeforePos.match(/\/\*\*\s*([\s\S]*?)\s*\*\/\s*$/);
899
+ if (!jsdocMatch) return null;
900
+
901
+ return this.parseJsDoc(jsdocMatch[1]);
902
+ }
903
+
904
+ private parseJsDoc(jsdoc: string): ParsedDocumentation {
905
+ const result: ParsedDocumentation = {
906
+ params: [],
907
+ examples: []
908
+ };
909
+
910
+ const lines = jsdoc.split('\n').map(l => l.replace(/^\s*\*\s?/, '').trim());
911
+ const descLines: string[] = [];
912
+
913
+ for (const line of lines) {
914
+ // @param {type} name - description or @param name {type} description
915
+ const paramMatch = line.match(/^@param\s+(?:\{([^}]+)\}\s+)?(\w+)\s*(?:-\s*)?(.*)$/) ||
916
+ line.match(/^@param\s+(\w+)\s+\{([^}]+)\}\s+(.*)$/);
917
+ if (paramMatch) {
918
+ const type = paramMatch[1]?.includes('}') ? paramMatch[2] : paramMatch[1];
919
+ const name = paramMatch[1]?.includes('}') ? paramMatch[1] : paramMatch[2];
920
+ result.params.push({
921
+ name,
922
+ type,
923
+ description: paramMatch[3],
924
+ required: !type?.includes('?') && !type?.includes('undefined')
925
+ });
926
+ continue;
927
+ }
928
+
929
+ const returnMatch = line.match(/^@returns?\s+(?:\{([^}]+)\}\s+)?(.*)$/);
930
+ if (returnMatch) {
931
+ result.returns = {
932
+ type: returnMatch[1],
933
+ description: returnMatch[2]
934
+ };
935
+ continue;
936
+ }
937
+
938
+ if (line.startsWith('@example')) {
939
+ continue; // Example content follows
940
+ }
941
+
942
+ if (line.startsWith('@')) {
943
+ // Other tags - skip
944
+ continue;
945
+ }
946
+
947
+ descLines.push(line);
948
+ }
949
+
950
+ result.description = descLines.filter(l => l).join(' ').trim();
951
+
952
+ return result;
953
+ }
954
+
955
+ extractFunctions(code: string, filename: string): ExtractedTool[] {
956
+ const tools: ExtractedTool[] = [];
957
+
958
+ // Match function/method definitions
959
+ const patterns = [
960
+ // async function name(params): ReturnType
961
+ /(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{]+))?\s*\{/g,
962
+ // const name = (params): ReturnType =>
963
+ /(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?\(([^)]*)\)(?:\s*:\s*([^=]+))?\s*=>/g,
964
+ // class method: async name(params): ReturnType
965
+ /(?:async\s+)?(\w+)\s*\(([^)]*)\)(?:\s*:\s*([^{]+))?\s*\{/g
966
+ ];
967
+
968
+ for (const pattern of patterns) {
969
+ let match;
970
+ while ((match = pattern.exec(code)) !== null) {
971
+ const [, funcName, paramsStr, returnType] = match;
972
+
973
+ // Skip constructors, private, and lifecycle methods
974
+ if (funcName === 'constructor' || funcName.startsWith('_')) continue;
975
+ if (['if', 'for', 'while', 'switch', 'catch'].includes(funcName)) continue;
976
+
977
+ const doc = this.parseDocumentation(code, match.index);
978
+ const params = this.parseTypeScriptParams(paramsStr, doc);
979
+
980
+ const { confidence, factors } = this.calculateConfidence(
981
+ doc,
982
+ returnType !== undefined || params.some(p => p.type !== undefined),
983
+ 'code'
984
+ );
985
+ const lineNum = code.substring(0, match.index).split('\n').length;
986
+
987
+ tools.push({
988
+ name: funcName,
989
+ description: doc?.description || `Function: ${funcName}`,
990
+ inputSchema: {
991
+ type: 'object',
992
+ properties: this.tsParamsToProperties(params),
993
+ required: params.filter(p => p.required).map(p => p.name)
994
+ },
995
+ source: {
996
+ type: 'code',
997
+ file: filename,
998
+ line: lineNum
999
+ },
1000
+ confidence,
1001
+ confidenceFactors: factors
1002
+ });
1003
+ }
1004
+ }
1005
+
1006
+ return tools;
1007
+ }
1008
+
1009
+ private parseTypeScriptParams(paramsStr: string, doc: ParsedDocumentation | null): Array<{
1010
+ name: string;
1011
+ type?: string;
1012
+ required: boolean;
1013
+ }> {
1014
+ const params: Array<{ name: string; type?: string; required: boolean }> = [];
1015
+
1016
+ if (!paramsStr.trim()) return params;
1017
+
1018
+ // Split handling generics
1019
+ const parts = this.splitParams(paramsStr);
1020
+
1021
+ for (const part of parts) {
1022
+ const trimmed = part.trim();
1023
+ if (!trimmed) continue;
1024
+
1025
+ // Match: name?: Type = default or destructured { a, b }: Type
1026
+ if (trimmed.startsWith('{')) continue; // Skip destructured for now
1027
+
1028
+ const match = trimmed.match(/^(\w+)(\??)(?:\s*:\s*([^=]+))?(?:\s*=\s*.+)?$/);
1029
+ if (match) {
1030
+ const [, name, optional, type] = match;
1031
+ const docParam = doc?.params.find(p => p.name === name);
1032
+
1033
+ params.push({
1034
+ name,
1035
+ type: type?.trim() || docParam?.type,
1036
+ required: !optional && !trimmed.includes('=')
1037
+ });
1038
+ }
1039
+ }
1040
+
1041
+ return params;
1042
+ }
1043
+
1044
+ private splitParams(paramsStr: string): string[] {
1045
+ const params: string[] = [];
1046
+ let depth = 0;
1047
+ let current = '';
1048
+
1049
+ for (const char of paramsStr) {
1050
+ if (char === '<' || char === '(' || char === '[' || char === '{') depth++;
1051
+ if (char === '>' || char === ')' || char === ']' || char === '}') depth--;
1052
+ if (char === ',' && depth === 0) {
1053
+ params.push(current);
1054
+ current = '';
1055
+ } else {
1056
+ current += char;
1057
+ }
1058
+ }
1059
+ if (current.trim()) params.push(current);
1060
+
1061
+ return params;
1062
+ }
1063
+
1064
+ private tsParamsToProperties(params: Array<{ name: string; type?: string }>): Record<string, any> {
1065
+ const props: Record<string, any> = {};
1066
+
1067
+ for (const param of params) {
1068
+ props[param.name] = {
1069
+ type: this.tsTypeToJson(param.type || ''),
1070
+ description: `${param.name} parameter`
1071
+ };
1072
+ }
1073
+
1074
+ return props;
1075
+ }
1076
+
1077
+ private tsTypeToJson(tsType: string): string {
1078
+ const t = tsType.toLowerCase().trim();
1079
+ if (t === 'string') return 'string';
1080
+ if (t === 'number') return 'number';
1081
+ if (t === 'boolean') return 'boolean';
1082
+ if (t.includes('[]') || t.startsWith('array')) return 'array';
1083
+ if (t === 'object' || t.startsWith('{')) return 'object';
1084
+ if (t === 'any' || t === 'unknown') return 'string';
1085
+ return 'string';
1086
+ }
1087
+ }
1088
+
1089
+ /**
1090
+ * Get appropriate parser for a file extension
1091
+ */
1092
+ export function getParserForLanguage(filename: string): BaseDocParser | null {
1093
+ const ext = filename.split('.').pop()?.toLowerCase();
1094
+
1095
+ switch (ext) {
1096
+ case 'py':
1097
+ return new PythonDocParser();
1098
+ case 'rs':
1099
+ return new RustDocParser();
1100
+ case 'go':
1101
+ return new GoDocParser();
1102
+ case 'rb':
1103
+ return new RubyDocParser();
1104
+ case 'ts':
1105
+ case 'tsx':
1106
+ case 'js':
1107
+ case 'jsx':
1108
+ case 'mjs':
1109
+ case 'cjs':
1110
+ return new TypeScriptDocParser();
1111
+ default:
1112
+ return null;
1113
+ }
1114
+ }