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.
- package/.env.example +8 -0
- package/.github/CODEOWNERS +6 -0
- package/.husky/pre-commit +1 -0
- package/.nvmrc +1 -0
- package/.prettierignore +5 -0
- package/.prettierrc +7 -0
- package/.vscode/settings.json +4 -0
- package/ARCHITECTURE.md +1429 -0
- package/CHANGELOG.md +167 -0
- package/CONTRIBUTING.md +327 -0
- package/LICENSE +201 -0
- package/README.md +1028 -0
- package/SECURITY.md +248 -0
- package/VISUAL_GUIDE.md +437 -0
- package/apps/vscode/IMPLEMENTATION.md +480 -0
- package/apps/vscode/README.md +248 -0
- package/apps/vscode/package.json +381 -0
- package/apps/vscode/resources/icon.png +0 -0
- package/apps/vscode/resources/icon.svg +5 -0
- package/apps/vscode/src/commands/browseRegistry.ts +211 -0
- package/apps/vscode/src/commands/configureClaudeDesktop.ts +332 -0
- package/apps/vscode/src/commands/convert.ts +82 -0
- package/apps/vscode/src/commands/convertCurrentRepo.ts +109 -0
- package/apps/vscode/src/commands/convertFromUrl.ts +138 -0
- package/apps/vscode/src/commands/index.ts +121 -0
- package/apps/vscode/src/commands/validate.ts +197 -0
- package/apps/vscode/src/extension.ts +464 -0
- package/apps/vscode/src/global.d.ts +36 -0
- package/apps/vscode/src/test/extension.test.ts +73 -0
- package/apps/vscode/src/utils/file-generator.ts +529 -0
- package/apps/vscode/src/utils/github-api.ts +335 -0
- package/apps/vscode/src/utils/index.ts +29 -0
- package/apps/vscode/src/utils/mcp-config.ts +334 -0
- package/apps/vscode/src/utils/storage.ts +87 -0
- package/apps/vscode/src/views/McpServersTreeView.ts +160 -0
- package/apps/vscode/src/views/OutputChannelView.ts +195 -0
- package/apps/vscode/src/views/StatusBarItem.ts +251 -0
- package/apps/vscode/src/views/ToolsExplorerView.ts +314 -0
- package/apps/vscode/src/views/historyProvider.ts +75 -0
- package/apps/vscode/src/views/index.ts +12 -0
- package/apps/vscode/src/views/resultsPanel.ts +330 -0
- package/apps/vscode/src/webviews/ConversionPanel.ts +350 -0
- package/apps/vscode/src/webviews/ToolDetailsPanel.ts +448 -0
- package/apps/vscode/src/webviews/index.ts +9 -0
- package/apps/vscode/src/webviews/webview-ui/styles.ts +492 -0
- package/apps/vscode/tsconfig.json +20 -0
- package/apps/web/PLAYGROUND_GUIDE.md +499 -0
- package/apps/web/README.md +505 -0
- package/apps/web/app/api/convert/route.ts +100 -0
- package/apps/web/app/api/convert/stream/route.ts +198 -0
- package/apps/web/app/api/deploy/route.ts +157 -0
- package/apps/web/app/api/edge/route.ts +308 -0
- package/apps/web/app/api/export-docker/route.ts +284 -0
- package/apps/web/app/api/generate-openapi/route.ts +119 -0
- package/apps/web/app/api/mcp/[serverId]/route.ts +263 -0
- package/apps/web/app/api/playground/connect/route.ts +143 -0
- package/apps/web/app/api/playground/disconnect/route.ts +78 -0
- package/apps/web/app/api/playground/execute/route.ts +135 -0
- package/apps/web/app/api/playground/sessions/route.ts +103 -0
- package/apps/web/app/api/playground/tools/route.ts +117 -0
- package/apps/web/app/api/playground/v2/connect/route.ts +96 -0
- package/apps/web/app/api/playground/v2/disconnect/route.ts +88 -0
- package/apps/web/app/api/playground/v2/health/route.ts +80 -0
- package/apps/web/app/api/playground/v2/prompts/route.ts +160 -0
- package/apps/web/app/api/playground/v2/resources/route.ts +159 -0
- package/apps/web/app/api/playground/v2/sessions/route.ts +184 -0
- package/apps/web/app/api/playground/v2/tools/route.ts +167 -0
- package/apps/web/app/api/stream/route.ts +232 -0
- package/apps/web/app/batch/BatchConvertClient.tsx +190 -0
- package/apps/web/app/batch/page.tsx +37 -0
- package/apps/web/app/convert/page.tsx +269 -0
- package/apps/web/app/dashboard/page.tsx +380 -0
- package/apps/web/app/globals.css +622 -0
- package/apps/web/app/layout.tsx +120 -0
- package/apps/web/app/manifest.ts +31 -0
- package/apps/web/app/opengraph-image.tsx +112 -0
- package/apps/web/app/page.old.tsx +924 -0
- package/apps/web/app/page.tsx +77 -0
- package/apps/web/app/playground/page.tsx +306 -0
- package/apps/web/app/playground/v2/error.tsx +163 -0
- package/apps/web/app/playground/v2/layout.tsx +58 -0
- package/apps/web/app/playground/v2/loading.tsx +152 -0
- package/apps/web/app/playground/v2/page.tsx +644 -0
- package/apps/web/app/playground/v2/providers.tsx +214 -0
- package/apps/web/app/playground/v2/use-shortcuts.ts +209 -0
- package/apps/web/app/playground/v2/use-url-state.ts +296 -0
- package/apps/web/app/providers.tsx +22 -0
- package/apps/web/app/sitemap.ts +32 -0
- package/apps/web/app/twitter-image.tsx +112 -0
- package/apps/web/components/BranchSelector.tsx +401 -0
- package/apps/web/components/ClaudeConfigExport.tsx +226 -0
- package/apps/web/components/Features.tsx +84 -0
- package/apps/web/components/Footer.tsx +119 -0
- package/apps/web/components/GenerationProgress.tsx +248 -0
- package/apps/web/components/GithubUrlInput.tsx +483 -0
- package/apps/web/components/Header.tsx +175 -0
- package/apps/web/components/Hero.tsx +117 -0
- package/apps/web/components/HowItWorks.tsx +119 -0
- package/apps/web/components/InstallBanner.tsx +158 -0
- package/apps/web/components/Logo.tsx +116 -0
- package/apps/web/components/ParticleBackground.tsx +105 -0
- package/apps/web/components/Playground.tsx +472 -0
- package/apps/web/components/PlaygroundToolTester.tsx +410 -0
- package/apps/web/components/ProductCards.tsx +179 -0
- package/apps/web/components/SplitView.tsx +194 -0
- package/apps/web/components/ToolFilter.tsx +260 -0
- package/apps/web/components/ToolList.tsx +325 -0
- package/apps/web/components/batch/BatchConvert.tsx +785 -0
- package/apps/web/components/batch/index.ts +7 -0
- package/apps/web/components/convert/ConfigTabs.tsx +230 -0
- package/apps/web/components/convert/ConversionResult.tsx +482 -0
- package/apps/web/components/convert/InlinePlayground.tsx +259 -0
- package/apps/web/components/convert/LoadingSteps.tsx +311 -0
- package/apps/web/components/convert/OneClickInstall.tsx +224 -0
- package/apps/web/components/convert/ToolCard.tsx +189 -0
- package/apps/web/components/convert/TryInPlayground.tsx +242 -0
- package/apps/web/components/convert/index.ts +12 -0
- package/apps/web/components/deploy/DeployButton.tsx +369 -0
- package/apps/web/components/deploy/index.ts +7 -0
- package/apps/web/components/docker/DockerExport.tsx +690 -0
- package/apps/web/components/docker/index.ts +7 -0
- package/apps/web/components/install/OneClickInstall.tsx +676 -0
- package/apps/web/components/install/index.ts +7 -0
- package/apps/web/components/playground/CapabilityTabs.tsx +150 -0
- package/apps/web/components/playground/ConnectionStatusV2.tsx +322 -0
- package/apps/web/components/playground/EmptyStates.tsx +305 -0
- package/apps/web/components/playground/ExecutionLog.tsx +260 -0
- package/apps/web/components/playground/ExecutionLogV2.tsx +378 -0
- package/apps/web/components/playground/JsonViewer.tsx +388 -0
- package/apps/web/components/playground/PlaygroundLayout.tsx +244 -0
- package/apps/web/components/playground/PromptsPanel.tsx +385 -0
- package/apps/web/components/playground/ResourcesPanel.tsx +378 -0
- package/apps/web/components/playground/SchemaForm.tsx +477 -0
- package/apps/web/components/playground/ServerStatus.tsx +151 -0
- package/apps/web/components/playground/ShareButton.tsx +239 -0
- package/apps/web/components/playground/ToolsPanel.tsx +309 -0
- package/apps/web/components/playground/TransportConfigurator.tsx +563 -0
- package/apps/web/components/playground/index.ts +74 -0
- package/apps/web/components/playground/types.ts +202 -0
- package/apps/web/components/streaming/StreamingProgress.tsx +441 -0
- package/apps/web/components/streaming/index.ts +7 -0
- package/apps/web/components/ui/badge.tsx +42 -0
- package/apps/web/components/ui/button.tsx +88 -0
- package/apps/web/components/ui/card.tsx +75 -0
- package/apps/web/components/ui/code-block.tsx +122 -0
- package/apps/web/components/ui/index.ts +12 -0
- package/apps/web/components/ui/input.tsx +55 -0
- package/apps/web/components/ui/tabs.tsx +61 -0
- package/apps/web/hooks/index.ts +85 -0
- package/apps/web/hooks/types.ts +1173 -0
- package/apps/web/hooks/use-conversion.ts +133 -0
- package/apps/web/hooks/use-execution-history.ts +376 -0
- package/apps/web/hooks/use-generation-progress.ts +147 -0
- package/apps/web/hooks/use-local-storage.ts +88 -0
- package/apps/web/hooks/use-mcp-client.ts +623 -0
- package/apps/web/hooks/use-mcp-connection.ts +500 -0
- package/apps/web/hooks/use-mcp-execution.ts +282 -0
- package/apps/web/hooks/use-mcp-prompts.ts +441 -0
- package/apps/web/hooks/use-mcp-resources.ts +430 -0
- package/apps/web/hooks/use-mcp-tools.ts +540 -0
- package/apps/web/hooks/use-playground-store.ts +299 -0
- package/apps/web/hooks/use-playground.ts +184 -0
- package/apps/web/hooks/use-streaming-conversion.ts +227 -0
- package/apps/web/hooks/useBatchConversion.ts +271 -0
- package/apps/web/hooks/useDockerConfig.ts +161 -0
- package/apps/web/hooks/usePlatformDetection.ts +80 -0
- package/apps/web/hooks/useStreaming.ts +199 -0
- package/apps/web/lib/api/errors.ts +386 -0
- package/apps/web/lib/api/index.ts +137 -0
- package/apps/web/lib/api/logger.ts +187 -0
- package/apps/web/lib/api/middleware.ts +364 -0
- package/apps/web/lib/api/openapi.ts +977 -0
- package/apps/web/lib/api/session-manager.ts +594 -0
- package/apps/web/lib/api/types.ts +433 -0
- package/apps/web/lib/api/validation.ts +523 -0
- package/apps/web/lib/constants.ts +114 -0
- package/apps/web/lib/mcp/client.ts +1137 -0
- package/apps/web/lib/mcp/events.ts +651 -0
- package/apps/web/lib/mcp/index.ts +347 -0
- package/apps/web/lib/mcp/logger.ts +428 -0
- package/apps/web/lib/mcp/metrics.ts +703 -0
- package/apps/web/lib/mcp/retry.ts +616 -0
- package/apps/web/lib/mcp/session-manager.ts +779 -0
- package/apps/web/lib/mcp/transports.ts +988 -0
- package/apps/web/lib/mcp/types.ts +594 -0
- package/apps/web/lib/mcp-client-enhanced.ts +871 -0
- package/apps/web/lib/mcp-client.ts +778 -0
- package/apps/web/lib/mcp-errors.ts +489 -0
- package/apps/web/lib/mcp-sandbox.ts +593 -0
- package/apps/web/lib/mcp-testing.ts +428 -0
- package/apps/web/lib/mcp-types.ts +448 -0
- package/apps/web/lib/playground-store.tsx +1147 -0
- package/apps/web/lib/utils.ts +439 -0
- package/apps/web/next-env.d.ts +5 -0
- package/apps/web/next.config.js +23 -0
- package/apps/web/package.json +55 -0
- package/apps/web/postcss.config.js +6 -0
- package/apps/web/public/.well-known/ai-plugin.json +17 -0
- package/apps/web/public/logo.svg +6 -0
- package/apps/web/public/robots.txt +22 -0
- package/apps/web/public/schema.json +27 -0
- package/apps/web/tailwind.config.js +26 -0
- package/apps/web/tailwind.config.ts +123 -0
- package/apps/web/tsconfig.json +20 -0
- package/apps/web/types/deploy.ts +139 -0
- package/apps/web/types/index.ts +247 -0
- package/apps/web/vercel.json +39 -0
- package/eslint.config.mjs +23 -0
- package/llms.txt +102 -0
- package/mkdocs/docs/api/core.md +318 -0
- package/mkdocs/docs/api/index.md +128 -0
- package/mkdocs/docs/api/mcp-server.md +301 -0
- package/mkdocs/docs/api/openapi-parser.md +254 -0
- package/mkdocs/docs/assets/logo.svg +7 -0
- package/mkdocs/docs/changelog.md +118 -0
- package/mkdocs/docs/cli/generate.md +148 -0
- package/mkdocs/docs/cli/index.md +52 -0
- package/mkdocs/docs/cli/inspect.md +164 -0
- package/mkdocs/docs/cli/serve.md +136 -0
- package/mkdocs/docs/concepts/classification.md +254 -0
- package/mkdocs/docs/concepts/how-it-works.md +299 -0
- package/mkdocs/docs/concepts/index.md +77 -0
- package/mkdocs/docs/concepts/mcp-protocol.md +362 -0
- package/mkdocs/docs/concepts/tool-types.md +382 -0
- package/mkdocs/docs/contributing/architecture.md +262 -0
- package/mkdocs/docs/contributing/development.md +245 -0
- package/mkdocs/docs/contributing/index.md +73 -0
- package/mkdocs/docs/contributing/testing.md +320 -0
- package/mkdocs/docs/getting-started/configuration.md +235 -0
- package/mkdocs/docs/getting-started/index.md +54 -0
- package/mkdocs/docs/getting-started/installation.md +145 -0
- package/mkdocs/docs/getting-started/quickstart.md +160 -0
- package/mkdocs/docs/guides/batch.md +375 -0
- package/mkdocs/docs/guides/claude-desktop.md +227 -0
- package/mkdocs/docs/guides/cursor.md +188 -0
- package/mkdocs/docs/guides/custom-tools.md +367 -0
- package/mkdocs/docs/guides/index.md +78 -0
- package/mkdocs/docs/guides/private-repos.md +221 -0
- package/mkdocs/docs/guides/vscode.md +247 -0
- package/mkdocs/docs/index.md +175 -0
- package/mkdocs/docs/reference/config.md +223 -0
- package/mkdocs/docs/reference/env.md +192 -0
- package/mkdocs/docs/reference/index.md +102 -0
- package/mkdocs/docs/reference/tools.md +309 -0
- package/mkdocs/docs/stylesheets/extra.css +231 -0
- package/mkdocs/mkdocs.yml +204 -0
- package/mkdocs/overrides/.gitkeep +1 -0
- package/mkdocs/overrides/main.html +7 -0
- package/mkdocs/python-deps.txt +7 -0
- package/mkdocs/vercel.json +11 -0
- package/package.json +63 -0
- package/packages/core/package.json +61 -0
- package/packages/core/src/__tests__/bitbucket-client.test.ts +366 -0
- package/packages/core/src/__tests__/cli.test.ts +235 -0
- package/packages/core/src/__tests__/code-extractor.test.ts +378 -0
- package/packages/core/src/__tests__/docker-generator.test.ts +255 -0
- package/packages/core/src/__tests__/github-client.test.ts +390 -0
- package/packages/core/src/__tests__/gitlab-client.test.ts +319 -0
- package/packages/core/src/__tests__/go-extractor.test.ts +351 -0
- package/packages/core/src/__tests__/graphql-extractor.test.ts +330 -0
- package/packages/core/src/__tests__/java-extractor.test.ts +497 -0
- package/packages/core/src/__tests__/plugins.test.ts +467 -0
- package/packages/core/src/__tests__/readme-extractor.test.ts +258 -0
- package/packages/core/src/__tests__/redis-cache.test.ts +307 -0
- package/packages/core/src/__tests__/rust-extractor.test.ts +252 -0
- package/packages/core/src/__tests__/streaming.test.ts +251 -0
- package/packages/core/src/additional-extractors.ts +333 -0
- package/packages/core/src/cache/cache-interface.ts +179 -0
- package/packages/core/src/cache/index.ts +210 -0
- package/packages/core/src/cache/redis-cache.ts +291 -0
- package/packages/core/src/cache/upstash-cache.ts +379 -0
- package/packages/core/src/cache.ts +251 -0
- package/packages/core/src/cli.ts +822 -0
- package/packages/core/src/code-extractor.ts +696 -0
- package/packages/core/src/docker-generator.ts +470 -0
- package/packages/core/src/edge-compatible.ts +491 -0
- package/packages/core/src/extractors/go-extractor.ts +791 -0
- package/packages/core/src/extractors/index.ts +9 -0
- package/packages/core/src/extractors/java-extractor.ts +937 -0
- package/packages/core/src/extractors/rust-extractor.ts +744 -0
- package/packages/core/src/github-client.ts +319 -0
- package/packages/core/src/go-generator.ts +356 -0
- package/packages/core/src/graphql-extractor.ts +358 -0
- package/packages/core/src/index.ts +797 -0
- package/packages/core/src/langchain-exporter.ts +617 -0
- package/packages/core/src/language-parsers.ts +1114 -0
- package/packages/core/src/mcp-introspector.ts +279 -0
- package/packages/core/src/monorepo-detector.ts +378 -0
- package/packages/core/src/plugins/index.ts +370 -0
- package/packages/core/src/plugins/registry.ts +404 -0
- package/packages/core/src/plugins/types.ts +215 -0
- package/packages/core/src/providers/base-provider.ts +246 -0
- package/packages/core/src/providers/bitbucket-client.ts +464 -0
- package/packages/core/src/providers/gitlab-client.ts +388 -0
- package/packages/core/src/providers/index.ts +176 -0
- package/packages/core/src/python-generator.ts +260 -0
- package/packages/core/src/queue/index.ts +100 -0
- package/packages/core/src/queue/memory-queue.ts +445 -0
- package/packages/core/src/queue/redis-queue.ts +578 -0
- package/packages/core/src/queue/types.ts +251 -0
- package/packages/core/src/readme-extractor.ts +409 -0
- package/packages/core/src/schema-generator.ts +638 -0
- package/packages/core/src/streaming.ts +999 -0
- package/packages/core/src/types.ts +289 -0
- package/packages/core/tsconfig.json +9 -0
- package/packages/core/tsup.config.ts +25 -0
- package/packages/mcp-server/README.md +297 -0
- package/packages/mcp-server/package.json +55 -0
- package/packages/mcp-server/src/__tests__/mcp-server.test.ts +177 -0
- package/packages/mcp-server/src/__tests__/tools.test.ts +217 -0
- package/packages/mcp-server/src/index.ts +1206 -0
- package/packages/mcp-server/src/prompts/index.ts +601 -0
- package/packages/mcp-server/src/tools/export-docker.ts +362 -0
- package/packages/mcp-server/src/tools/generate-openapi.ts +162 -0
- package/packages/mcp-server/src/tools/monitor-mcp-server.ts +448 -0
- package/packages/mcp-server/src/tools/stream-convert.ts +398 -0
- package/packages/mcp-server/src/tools/test-mcp-tool.ts +531 -0
- package/packages/mcp-server/tsconfig.json +12 -0
- package/packages/mcp-server/tsup.config.ts +14 -0
- package/packages/openapi-parser/package-lock.json +3028 -0
- package/packages/openapi-parser/package.json +41 -0
- package/packages/openapi-parser/src/analyzer.ts +700 -0
- package/packages/openapi-parser/src/asyncapi-parser.ts +475 -0
- package/packages/openapi-parser/src/cli.ts +302 -0
- package/packages/openapi-parser/src/generator.ts +570 -0
- package/packages/openapi-parser/src/generators/express-analyzer.ts +649 -0
- package/packages/openapi-parser/src/generators/fastapi-analyzer.ts +960 -0
- package/packages/openapi-parser/src/generators/index.ts +200 -0
- package/packages/openapi-parser/src/generators/nextjs-analyzer.ts +768 -0
- package/packages/openapi-parser/src/generators/openapi-builder.ts +527 -0
- package/packages/openapi-parser/src/generators/types.ts +298 -0
- package/packages/openapi-parser/src/graphql-parser.ts +462 -0
- package/packages/openapi-parser/src/grpc-parser.ts +649 -0
- package/packages/openapi-parser/src/har-parser.ts +723 -0
- package/packages/openapi-parser/src/index.ts +635 -0
- package/packages/openapi-parser/src/insomnia-parser.ts +614 -0
- package/packages/openapi-parser/src/parser.ts +231 -0
- package/packages/openapi-parser/src/postman-parser.ts +611 -0
- package/packages/openapi-parser/src/ref-resolver.ts +313 -0
- package/packages/openapi-parser/src/transformer.ts +459 -0
- package/packages/openapi-parser/tests/generators/express.test.ts +209 -0
- package/packages/openapi-parser/tests/generators/fastapi.test.ts +236 -0
- package/packages/openapi-parser/tests/generators/nextjs.test.ts +273 -0
- package/packages/openapi-parser/tests/parsers.test.ts +847 -0
- package/packages/openapi-parser/tsconfig.json +9 -0
- package/packages/openapi-parser/tsup.config.ts +11 -0
- package/packages/registry/package.json +59 -0
- package/packages/registry/src/cli.ts +456 -0
- package/packages/registry/src/index.ts +44 -0
- package/packages/registry/src/popular/github.json +47 -0
- package/packages/registry/src/popular/index.ts +55 -0
- package/packages/registry/src/popular/linear.json +42 -0
- package/packages/registry/src/popular/notion.json +42 -0
- package/packages/registry/src/popular/openai.json +40 -0
- package/packages/registry/src/popular/resend.json +38 -0
- package/packages/registry/src/popular/slack.json +42 -0
- package/packages/registry/src/popular/stripe.json +163 -0
- package/packages/registry/src/popular/supabase.json +42 -0
- package/packages/registry/src/popular/twilio.json +40 -0
- package/packages/registry/src/popular/vercel.json +40 -0
- package/packages/registry/src/registry.ts +492 -0
- package/packages/registry/src/storage.ts +334 -0
- package/packages/registry/src/types.ts +275 -0
- package/packages/registry/src/updater.ts +208 -0
- package/packages/registry/tsconfig.json +10 -0
- package/packages/registry/tsup.config.ts +11 -0
- package/pnpm-workspace.yaml +3 -0
- package/scripts/build-docs.sh +16 -0
- package/server.json +9 -0
- package/templates/Dockerfile.python.template +60 -0
- package/templates/Dockerfile.typescript.template +60 -0
- package/templates/docker-compose.template.yml +68 -0
- package/tests/fixtures/express-app/index.js +34 -0
- package/tests/fixtures/express-app/routes/posts.js +43 -0
- package/tests/fixtures/express-app/routes/users.js +58 -0
- package/tests/fixtures/fastapi-app/main.py +125 -0
- package/tests/fixtures/fastapi-app/routes/admin.py +42 -0
- package/tests/fixtures/graphql/simple-schema.graphql +65 -0
- package/tests/fixtures/mocks/github-api-responses.json +63 -0
- package/tests/fixtures/nextjs-app/app/api/posts/route.ts +55 -0
- package/tests/fixtures/nextjs-app/app/api/users/[id]/route.ts +63 -0
- package/tests/fixtures/nextjs-app/app/api/users/route.ts +44 -0
- package/tests/fixtures/nextjs-app/pages/api/health.ts +28 -0
- package/tests/fixtures/openapi/petstore.yaml +179 -0
- package/tests/integration/langchain-export.test.ts +405 -0
- package/tests/integration/openapi-conversion.test.ts +221 -0
- package/tsconfig.json +18 -0
- 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
|
+
});
|