@crownpeak/dqm-react-component-dev-mcp 1.2.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/README.md +138 -0
- package/data/.env.example +22 -0
- package/data/.gitattributes +47 -0
- package/data/.glfrc.json +7 -0
- package/data/.husky/pre-commit +5 -0
- package/data/.nvmrc +1 -0
- package/data/CHANGELOG.md +75 -0
- package/data/CODE_OF_CONDUCT.md +129 -0
- package/data/CONTRIBUTING.md +203 -0
- package/data/DOCS-STRUCTURE.md +307 -0
- package/data/I18N.md +292 -0
- package/data/LICENSE +22 -0
- package/data/README.md +315 -0
- package/data/SECURITY.md +125 -0
- package/data/WIKI-DEPLOYMENT.md +348 -0
- package/data/docs/AI-FEATURES.md +610 -0
- package/data/docs/API-REFERENCE.md +1022 -0
- package/data/docs/AUTHENTICATION.md +301 -0
- package/data/docs/BACKEND-API.md +468 -0
- package/data/docs/DEVELOPMENT.md +375 -0
- package/data/docs/EXAMPLES.md +622 -0
- package/data/docs/MCP-SERVER.md +307 -0
- package/data/docs/MIGRATION-GUIDE.md +367 -0
- package/data/docs/NPM-PUBLISH.md +193 -0
- package/data/docs/QUICKSTART.md +206 -0
- package/data/docs/REDIS-SETUP.md +162 -0
- package/data/docs/SERVER.md +228 -0
- package/data/docs/TROUBLESHOOTING.md +657 -0
- package/data/docs/WIDGET-GUIDE.md +638 -0
- package/data/docs/WIKI-HOME.md +58 -0
- package/data/docs/WIKI-SIDEBAR.md +39 -0
- package/data/package.json +171 -0
- package/data/playwright.config.ts +64 -0
- package/data/probe/.cargo/config.toml +10 -0
- package/data/probe/.claude/commands/performance-review.md +15 -0
- package/data/probe/.clinerules +288 -0
- package/data/probe/.dockerignore +57 -0
- package/data/probe/.githooks/post-commit +11 -0
- package/data/probe/.githooks/pre-commit +99 -0
- package/data/probe/.githooks/pre-commit-vow +9 -0
- package/data/probe/.prompts/engineer.md +41 -0
- package/data/probe/.roomodes +28 -0
- package/data/probe/.windsurfrules +0 -0
- package/data/probe/BASH_TOOL_SUMMARY.md +148 -0
- package/data/probe/BENCHMARKING.md +256 -0
- package/data/probe/CLAUDE.md +226 -0
- package/data/probe/CODE_OF_CONDUCT.md +128 -0
- package/data/probe/CONTRIBUTING.md +193 -0
- package/data/probe/Cargo.toml +120 -0
- package/data/probe/Cross.toml +10 -0
- package/data/probe/DOCKER-README.md +224 -0
- package/data/probe/Dockerfile +32 -0
- package/data/probe/ENHANCED_DEBUG_TELEMETRY.md +188 -0
- package/data/probe/LICENSE +201 -0
- package/data/probe/Makefile +210 -0
- package/data/probe/README.md +824 -0
- package/data/probe/SECURITY.md +67 -0
- package/data/probe/WINDOWS-GUIDE.md +294 -0
- package/data/probe/benches/parsing_benchmarks.rs +370 -0
- package/data/probe/benches/search_benchmarks.rs +599 -0
- package/data/probe/benches/simd_benchmarks.rs +372 -0
- package/data/probe/benches/timing_benchmarks.rs +287 -0
- package/data/probe/build-windows.bat +229 -0
- package/data/probe/codex-config/config.toml +6 -0
- package/data/probe/docs/PERFORMANCE_OPTIMIZATION.md +161 -0
- package/data/probe/examples/cache_demo.rs +46 -0
- package/data/probe/examples/chat/.dockerignore +37 -0
- package/data/probe/examples/chat/ChatSessionManager.js +295 -0
- package/data/probe/examples/chat/Dockerfile +98 -0
- package/data/probe/examples/chat/LICENSE +201 -0
- package/data/probe/examples/chat/LOCAL_IMAGE_SUPPORT.md +195 -0
- package/data/probe/examples/chat/MCP_INTEGRATION.md +400 -0
- package/data/probe/examples/chat/README.md +338 -0
- package/data/probe/examples/chat/TRACING.md +226 -0
- package/data/probe/examples/chat/appTracer.js +968 -0
- package/data/probe/examples/chat/auth.js +76 -0
- package/data/probe/examples/chat/bin/probe-chat.js +13 -0
- package/data/probe/examples/chat/build.js +104 -0
- package/data/probe/examples/chat/cancelRequest.js +84 -0
- package/data/probe/examples/chat/demo-agentic-image-flow.js +88 -0
- package/data/probe/examples/chat/demo-local-images.js +128 -0
- package/data/probe/examples/chat/fileSpanExporter.js +181 -0
- package/data/probe/examples/chat/implement/README.md +228 -0
- package/data/probe/examples/chat/implement/backends/AiderBackend.js +750 -0
- package/data/probe/examples/chat/implement/backends/BaseBackend.js +276 -0
- package/data/probe/examples/chat/implement/backends/ClaudeCodeBackend.js +767 -0
- package/data/probe/examples/chat/implement/backends/MockBackend.js +237 -0
- package/data/probe/examples/chat/implement/backends/registry.js +85 -0
- package/data/probe/examples/chat/implement/core/BackendManager.js +567 -0
- package/data/probe/examples/chat/implement/core/ImplementTool.js +354 -0
- package/data/probe/examples/chat/implement/core/config.js +428 -0
- package/data/probe/examples/chat/implement/core/timeouts.js +58 -0
- package/data/probe/examples/chat/implement/core/utils.js +496 -0
- package/data/probe/examples/chat/implement/types/BackendTypes.js +126 -0
- package/data/probe/examples/chat/index.js +669 -0
- package/data/probe/examples/chat/mcpServer.js +341 -0
- package/data/probe/examples/chat/npm/LICENSE +15 -0
- package/data/probe/examples/chat/npm/README.md +168 -0
- package/data/probe/examples/chat/npm/bin/probe-chat.js +156 -0
- package/data/probe/examples/chat/npm/index.js +259 -0
- package/data/probe/examples/chat/npm/package.json +54 -0
- package/data/probe/examples/chat/package.json +102 -0
- package/data/probe/examples/chat/probeChat.js +456 -0
- package/data/probe/examples/chat/probeTool.js +491 -0
- package/data/probe/examples/chat/storage/JsonChatStorage.js +476 -0
- package/data/probe/examples/chat/telemetry.js +281 -0
- package/data/probe/examples/chat/test/integration/chatFlows.test.js +320 -0
- package/data/probe/examples/chat/test/integration/toolCalling.test.js +471 -0
- package/data/probe/examples/chat/test/mocks/mockLLMProvider.js +269 -0
- package/data/probe/examples/chat/test/test-backends.js +90 -0
- package/data/probe/examples/chat/test/testUtils.js +530 -0
- package/data/probe/examples/chat/test/unit/backendTimeout.test.js +161 -0
- package/data/probe/examples/chat/test/unit/packageFiles.test.js +120 -0
- package/data/probe/examples/chat/test/verify-tests.js +118 -0
- package/data/probe/examples/chat/test-agentic-image-loading.js +294 -0
- package/data/probe/examples/chat/test-ai-sdk-telemetry.js +204 -0
- package/data/probe/examples/chat/test-chat-tracing.js +38 -0
- package/data/probe/examples/chat/test-direct-function.js +49 -0
- package/data/probe/examples/chat/test-file-size-validation.js +103 -0
- package/data/probe/examples/chat/test-full-mcp-integration.js +258 -0
- package/data/probe/examples/chat/test-github-context.txt +12 -0
- package/data/probe/examples/chat/test-hierarchy.js +203 -0
- package/data/probe/examples/chat/test-image-spans.js +37 -0
- package/data/probe/examples/chat/test-local-image-reading.js +176 -0
- package/data/probe/examples/chat/test-mcp-integration.js +136 -0
- package/data/probe/examples/chat/test-mcp-probe-server.js +161 -0
- package/data/probe/examples/chat/test-mcp-with-ai.js +279 -0
- package/data/probe/examples/chat/test-multiple-allowed-dirs.js +111 -0
- package/data/probe/examples/chat/test-probe-mcp-server.js +110 -0
- package/data/probe/examples/chat/test-security-validation.js +145 -0
- package/data/probe/examples/chat/test-simple-tracing.js +32 -0
- package/data/probe/examples/chat/test-trace-verification.js +235 -0
- package/data/probe/examples/chat/test-tracing.js +114 -0
- package/data/probe/examples/chat/tokenCounter.js +419 -0
- package/data/probe/examples/chat/tokenUsageDisplay.js +134 -0
- package/data/probe/examples/chat/webServer.js +1103 -0
- package/data/probe/examples/reranker/Cargo.toml +33 -0
- package/data/probe/examples/reranker/DEBUG_OUTPUT_ANALYSIS.md +71 -0
- package/data/probe/examples/reranker/MODELS.md +66 -0
- package/data/probe/examples/reranker/MODEL_COMPARISON.md +60 -0
- package/data/probe/examples/reranker/MULTI_MODEL_ANALYSIS.md +176 -0
- package/data/probe/examples/reranker/PERFORMANCE_SUMMARY.md +156 -0
- package/data/probe/examples/reranker/README.md +347 -0
- package/data/probe/examples/reranker/RUST_BERT_COMPARISON.md +82 -0
- package/data/probe/examples/reranker/TOKENIZATION_GUIDE.md +120 -0
- package/data/probe/examples/reranker/check_rust_tokenizer.py +108 -0
- package/data/probe/examples/reranker/convert_to_torchscript.py +109 -0
- package/data/probe/examples/reranker/debug_scoring.py +189 -0
- package/data/probe/examples/reranker/debug_tokenization.py +154 -0
- package/data/probe/examples/reranker/download_models.sh +73 -0
- package/data/probe/examples/reranker/requirements.txt +13 -0
- package/data/probe/examples/reranker/run_comprehensive_benchmark.sh +83 -0
- package/data/probe/examples/reranker/rust_bert_test/Cargo.toml +12 -0
- package/data/probe/examples/reranker/rust_bert_test/README.md +54 -0
- package/data/probe/examples/reranker/simple_test.py +50 -0
- package/data/probe/examples/reranker/test_all_models.sh +63 -0
- package/data/probe/examples/reranker/test_bert_results.sh +44 -0
- package/data/probe/examples/reranker/test_cross_encoder.py +334 -0
- package/data/probe/examples/reranker/test_cross_encoder.sh +80 -0
- package/data/probe/examples/reranker/test_exact_comparison.py +151 -0
- package/data/probe/examples/reranker/test_parallel_performance.sh +56 -0
- package/data/probe/examples/reranker/test_scores.py +132 -0
- package/data/probe/install.ps1 +508 -0
- package/data/probe/install.sh +460 -0
- package/data/probe/npm/CLONE_METHOD_EXAMPLES.md +596 -0
- package/data/probe/npm/CONTEXT_COMPACTION.md +303 -0
- package/data/probe/npm/DELEGATE_TOOL_README.md +166 -0
- package/data/probe/npm/MAID_INTEGRATION.md +313 -0
- package/data/probe/npm/MCP_INTEGRATION_SUMMARY.md +241 -0
- package/data/probe/npm/README.md +824 -0
- package/data/probe/npm/bin/.gitignore +7 -0
- package/data/probe/npm/bin/.gitkeep +0 -0
- package/data/probe/npm/bin/README.md +12 -0
- package/data/probe/npm/bin/probe +167 -0
- package/data/probe/npm/docs/CLAUDE_CODE_INTEGRATION.md +414 -0
- package/data/probe/npm/docs/CODEX_INTEGRATION.md +502 -0
- package/data/probe/npm/docs/EDIT_CREATE_TOOLS.md +233 -0
- package/data/probe/npm/docs/RETRY_AND_FALLBACK.md +674 -0
- package/data/probe/npm/example-usage.js +335 -0
- package/data/probe/npm/examples/multi-engine-demo.js +117 -0
- package/data/probe/npm/examples/probe-agent-cli.js +113 -0
- package/data/probe/npm/examples/test-agent-edit.js +114 -0
- package/data/probe/npm/examples/test-edit-create.js +120 -0
- package/data/probe/npm/examples/test-edit-direct.js +114 -0
- package/data/probe/npm/index.d.ts +744 -0
- package/data/probe/npm/jest.config.js +52 -0
- package/data/probe/npm/package.json +117 -0
- package/data/probe/npm/scripts/build-agent.cjs +75 -0
- package/data/probe/npm/scripts/build-cjs.js +124 -0
- package/data/probe/npm/scripts/build-mcp.cjs +36 -0
- package/data/probe/npm/scripts/postinstall.js +216 -0
- package/data/probe/npm/test-codex-e2e.js +78 -0
- package/data/probe/npm/test-download-lock.js +109 -0
- package/data/probe/npm/test-grep-security.js +94 -0
- package/data/probe/npm/test-grep-simplified.js +63 -0
- package/data/probe/npm/test-grep.js +51 -0
- package/data/probe/npm/tests/README.md +96 -0
- package/data/probe/npm/tests/agent-compact-history.test.js +174 -0
- package/data/probe/npm/tests/allow-tests-default.test.js +151 -0
- package/data/probe/npm/tests/contextCompactor.test.js +498 -0
- package/data/probe/npm/tests/delegate-config.test.js +353 -0
- package/data/probe/npm/tests/delegate-integration.test.js +348 -0
- package/data/probe/npm/tests/extractor-integration.test.js +162 -0
- package/data/probe/npm/tests/extractor.test.js +317 -0
- package/data/probe/npm/tests/fixtures/sampleDiagrams.js +267 -0
- package/data/probe/npm/tests/integration/claude-code-auto-fallback.spec.js +148 -0
- package/data/probe/npm/tests/integration/claude-code-multi-step.spec.js +127 -0
- package/data/probe/npm/tests/integration/claude-code-tool-events.spec.js +163 -0
- package/data/probe/npm/tests/integration/codex-auto-fallback.spec.js +191 -0
- package/data/probe/npm/tests/integration/codex-tool-events.spec.js +147 -0
- package/data/probe/npm/tests/integration/examplesChatMcp.test.js +402 -0
- package/data/probe/npm/tests/integration/mcpDotenvSupport.test.js +174 -0
- package/data/probe/npm/tests/integration/mcpErrorHandling.test.js +566 -0
- package/data/probe/npm/tests/integration/mcpRobustness.test.js +564 -0
- package/data/probe/npm/tests/integration/mcpStdoutPurity.test.js +355 -0
- package/data/probe/npm/tests/integration/probeAgentMcp.test.js +398 -0
- package/data/probe/npm/tests/integration/retryFallback.test.js +368 -0
- package/data/probe/npm/tests/integration/schema-in-initial-message.test.js +318 -0
- package/data/probe/npm/tests/integration/schema-validation-loop-prevention.test.js +244 -0
- package/data/probe/npm/tests/integration/schemaRetryLogic.test.js +94 -0
- package/data/probe/npm/tests/integration/validationFlow.test.js +329 -0
- package/data/probe/npm/tests/manual/test-codex-basic.js +110 -0
- package/data/probe/npm/tests/mcp/mcpClientManager.test.js +614 -0
- package/data/probe/npm/tests/mcp/mcpConfig.test.js +359 -0
- package/data/probe/npm/tests/mcp/mcpXmlBridge.test.js +436 -0
- package/data/probe/npm/tests/mcp/mockMcpServer.js +510 -0
- package/data/probe/npm/tests/mcp-strict-syntax.test.js +319 -0
- package/data/probe/npm/tests/mermaidQuoteEscaping.test.js +214 -0
- package/data/probe/npm/tests/nestedQuoteFix.test.js +40 -0
- package/data/probe/npm/tests/setup.js +46 -0
- package/data/probe/npm/tests/unit/allowed-tools.test.js +513 -0
- package/data/probe/npm/tests/unit/attempt-completion-closing-tag-in-content.test.js +188 -0
- package/data/probe/npm/tests/unit/attemptCompletionJsonFix.test.js +238 -0
- package/data/probe/npm/tests/unit/attemptCompletionJsonIssue.test.js +128 -0
- package/data/probe/npm/tests/unit/backtickAutoFix.test.js +35 -0
- package/data/probe/npm/tests/unit/bash-probe-agent-integration.test.js +389 -0
- package/data/probe/npm/tests/unit/bash-simple-commands.test.js +324 -0
- package/data/probe/npm/tests/unit/bash-tool-comprehensive.test.js +371 -0
- package/data/probe/npm/tests/unit/bash-tool-integration.test.js +310 -0
- package/data/probe/npm/tests/unit/bash-tool.test.js +341 -0
- package/data/probe/npm/tests/unit/completion-prompt.test.js +379 -0
- package/data/probe/npm/tests/unit/cwd-path-options.test.js +287 -0
- package/data/probe/npm/tests/unit/delegate-limits.test.js +422 -0
- package/data/probe/npm/tests/unit/direct-content-attempt-completion.test.js +235 -0
- package/data/probe/npm/tests/unit/edit-create-tools.test.js +609 -0
- package/data/probe/npm/tests/unit/enhancedMermaidValidation.test.js +577 -0
- package/data/probe/npm/tests/unit/extract-content.test.js +83 -0
- package/data/probe/npm/tests/unit/extract-multiple-targets.test.js +89 -0
- package/data/probe/npm/tests/unit/fallbackManager.test.js +442 -0
- package/data/probe/npm/tests/unit/githubCompatibilityValidation.test.js +258 -0
- package/data/probe/npm/tests/unit/imageConfig.test.js +149 -0
- package/data/probe/npm/tests/unit/imagePathResolution.test.js +345 -0
- package/data/probe/npm/tests/unit/json-fixing-agent.test.js +238 -0
- package/data/probe/npm/tests/unit/json-validation-enhanced-errors.test.js +199 -0
- package/data/probe/npm/tests/unit/jsonValidationInfiniteLoopFix.test.js +228 -0
- package/data/probe/npm/tests/unit/maidIntegration.test.js +139 -0
- package/data/probe/npm/tests/unit/maxIterationsWarning.test.js +195 -0
- package/data/probe/npm/tests/unit/mermaidEdgeLabelFix.test.js +161 -0
- package/data/probe/npm/tests/unit/mermaidHtmlEntities.test.js +76 -0
- package/data/probe/npm/tests/unit/mermaidInfiniteLoopFix.test.js +64 -0
- package/data/probe/npm/tests/unit/mermaidValidation.test.js +723 -0
- package/data/probe/npm/tests/unit/mermaidValidationVisorExample.test.js +309 -0
- package/data/probe/npm/tests/unit/probe-agent-clone-realistic.test.js +643 -0
- package/data/probe/npm/tests/unit/probe-agent-clone.test.js +476 -0
- package/data/probe/npm/tests/unit/probe-agent-delegate.test.js +400 -0
- package/data/probe/npm/tests/unit/probe-agent-model-option.test.js +118 -0
- package/data/probe/npm/tests/unit/probeTool-security.test.js +283 -0
- package/data/probe/npm/tests/unit/readImageTool.test.js +418 -0
- package/data/probe/npm/tests/unit/retryManager.test.js +317 -0
- package/data/probe/npm/tests/unit/schema-aware-reminders.test.js +288 -0
- package/data/probe/npm/tests/unit/schemaDefinitionDetection.test.js +115 -0
- package/data/probe/npm/tests/unit/schemaUtils.test.js +1268 -0
- package/data/probe/npm/tests/unit/simpleTelemetry.test.js +282 -0
- package/data/probe/npm/tests/unit/simplified-attempt-completion.test.js +274 -0
- package/data/probe/npm/tests/unit/single-quote-json-bug.test.js +231 -0
- package/data/probe/npm/tests/unit/subgraphAutoFix.test.js +110 -0
- package/data/probe/npm/tests/unit/system-prompt.test.js +32 -0
- package/data/probe/npm/tests/unit/types-probe-agent-options.test.js +42 -0
- package/data/probe/npm/tests/unit/xmlParsing.test.js +720 -0
- package/data/probe/npm/tsconfig.json +21 -0
- package/data/probe/result1.txt +19 -0
- package/data/probe/result2.txt +26 -0
- package/data/probe/scripts/benchmark.sh +270 -0
- package/data/probe/scripts/cache_memory_analysis.rs +844 -0
- package/data/probe/scripts/claude-hook-wrapper.sh +56 -0
- package/data/probe/site/.env.example +10 -0
- package/data/probe/site/DEPLOYMENT.md +86 -0
- package/data/probe/site/README.md +183 -0
- package/data/probe/site/adding-languages.md +135 -0
- package/data/probe/site/ai-chat.md +427 -0
- package/data/probe/site/ai-integration.md +1488 -0
- package/data/probe/site/blog/agentic-flow-custom-xml-protocol.md +407 -0
- package/data/probe/site/blog/index.md +118 -0
- package/data/probe/site/blog/v0.6.0-release.md +426 -0
- package/data/probe/site/blog.md +8 -0
- package/data/probe/site/changelog.md +200 -0
- package/data/probe/site/cli-mode.md +437 -0
- package/data/probe/site/code-extraction.md +436 -0
- package/data/probe/site/contributing/README.md +9 -0
- package/data/probe/site/contributing/documentation-cross-references.md +215 -0
- package/data/probe/site/contributing/documentation-maintenance.md +275 -0
- package/data/probe/site/contributing/documentation-structure.md +75 -0
- package/data/probe/site/documentation-cross-references.md +215 -0
- package/data/probe/site/documentation-guide.md +132 -0
- package/data/probe/site/documentation-maintenance.md +275 -0
- package/data/probe/site/features.md +147 -0
- package/data/probe/site/how-it-works.md +118 -0
- package/data/probe/site/index.md +175 -0
- package/data/probe/site/index.md.bak +133 -0
- package/data/probe/site/installation.md +235 -0
- package/data/probe/site/integrations/docker.md +248 -0
- package/data/probe/site/integrations/github-actions.md +413 -0
- package/data/probe/site/language-support-overview.md +168 -0
- package/data/probe/site/mcp-integration.md +587 -0
- package/data/probe/site/mcp-server.md +304 -0
- package/data/probe/site/navigation-structure.md +76 -0
- package/data/probe/site/nodejs-sdk.md +798 -0
- package/data/probe/site/output-formats.md +625 -0
- package/data/probe/site/package.json +21 -0
- package/data/probe/site/public/_headers +28 -0
- package/data/probe/site/public/_redirects +11 -0
- package/data/probe/site/quick-start.md +289 -0
- package/data/probe/site/search-functionality.md +291 -0
- package/data/probe/site/search-reference.md +291 -0
- package/data/probe/site/supported-languages.md +215 -0
- package/data/probe/site/use-cases/README.md +8 -0
- package/data/probe/site/use-cases/advanced-cli.md +253 -0
- package/data/probe/site/use-cases/ai-code-editors.md +239 -0
- package/data/probe/site/use-cases/building-ai-tools.md +529 -0
- package/data/probe/site/use-cases/cli-ai-workflows.md +285 -0
- package/data/probe/site/use-cases/deploying-probe-web-interface.md +255 -0
- package/data/probe/site/use-cases/integrating-probe-into-ai-code-editors.md +161 -0
- package/data/probe/site/use-cases/nodejs-sdk.md +596 -0
- package/data/probe/site/use-cases/team-chat.md +350 -0
- package/data/probe/site/web-interface.md +434 -0
- package/data/probe/site/wrangler.toml +9 -0
- package/data/probe/test-api-key.sh +1 -0
- package/data/probe/test-probe-implementation/hello.js +7 -0
- package/data/probe/test_cases/demonstrate_early_termination_issues.sh +176 -0
- package/data/probe/test_cases/early_termination_issues.rs +533 -0
- package/data/probe/test_data/test_nested_struct.go +26 -0
- package/data/probe/tests/README.md +286 -0
- package/data/probe/tests/README_search_determinism_tests.md +116 -0
- package/data/probe/tests/adjacent_comment_test.rs +152 -0
- package/data/probe/tests/apostrophe_handling_tests.rs +132 -0
- package/data/probe/tests/block_filtering_with_ast_tests.rs +669 -0
- package/data/probe/tests/block_merging_tests.rs +396 -0
- package/data/probe/tests/c_outline_format_tests.rs +2179 -0
- package/data/probe/tests/cache_invalidation_issues.rs.disabled +682 -0
- package/data/probe/tests/cache_order_tests.rs +147 -0
- package/data/probe/tests/cache_query_scoping_tests.rs +221 -0
- package/data/probe/tests/cli_tests.rs +680 -0
- package/data/probe/tests/comment_context_integration_test.rs +240 -0
- package/data/probe/tests/common.rs +33 -0
- package/data/probe/tests/complex_block_merging_tests.rs +599 -0
- package/data/probe/tests/complex_query_block_filtering_tests.rs +422 -0
- package/data/probe/tests/control_flow_closing_braces_test.rs +91 -0
- package/data/probe/tests/cpp_outline_format_tests.rs +1507 -0
- package/data/probe/tests/csharp_outline_format_tests.rs +941 -0
- package/data/probe/tests/elastic_query_integration_tests.rs +922 -0
- package/data/probe/tests/extract_command_tests.rs +1848 -0
- package/data/probe/tests/extract_deduplication_tests.rs +146 -0
- package/data/probe/tests/extract_input_file_tests.rs +84 -0
- package/data/probe/tests/extract_prompt_tests.rs +102 -0
- package/data/probe/tests/filename_search_tests.rs +96 -0
- package/data/probe/tests/fixtures/user/AssemblyInfo.cs +3 -0
- package/data/probe/tests/github_extract_tests.rs +234 -0
- package/data/probe/tests/go_comment_test.rs +253 -0
- package/data/probe/tests/go_outline_format_tests.rs +2587 -0
- package/data/probe/tests/go_path_resolver_tests.rs +96 -0
- package/data/probe/tests/html_outline_format_tests.rs +637 -0
- package/data/probe/tests/integration_tests.rs +837 -0
- package/data/probe/tests/ip_whitelist_test.rs +148 -0
- package/data/probe/tests/java_outline_format_tests.rs +1611 -0
- package/data/probe/tests/javascript_extract_tests.rs +315 -0
- package/data/probe/tests/javascript_outline_format_tests.rs +1464 -0
- package/data/probe/tests/json_format_tests.rs +436 -0
- package/data/probe/tests/json_schema_validation_tests.rs +450 -0
- package/data/probe/tests/lib_usage.rs +60 -0
- package/data/probe/tests/line_comment_context_extension_test.rs +459 -0
- package/data/probe/tests/line_map_cache_tests.rs +114 -0
- package/data/probe/tests/markdown_integration_tests.rs +190 -0
- package/data/probe/tests/mocks/test_ip_whitelist.go +11 -0
- package/data/probe/tests/mocks/test_object.js +27 -0
- package/data/probe/tests/mocks/test_struct.go +50 -0
- package/data/probe/tests/multi_keyword_pattern_tests.rs +464 -0
- package/data/probe/tests/multi_language_syntax_integration_tests.rs +218 -0
- package/data/probe/tests/multiple_capture_groups_tests.rs +169 -0
- package/data/probe/tests/negative_compound_word_tests.rs +246 -0
- package/data/probe/tests/nested_symbol_extraction_tests.rs +99 -0
- package/data/probe/tests/outline_cross_file_interference_test.rs +335 -0
- package/data/probe/tests/outline_keyword_preservation_test.rs +67 -0
- package/data/probe/tests/output_format_edge_cases_tests.rs +693 -0
- package/data/probe/tests/parallel_extraction_tests.rs +178 -0
- package/data/probe/tests/parallel_search_tests.rs +355 -0
- package/data/probe/tests/path_resolver_tests.rs +698 -0
- package/data/probe/tests/php_outline_format_extended_tests.rs +928 -0
- package/data/probe/tests/php_outline_format_tests.rs +768 -0
- package/data/probe/tests/property_tests.proptest-regressions +9 -0
- package/data/probe/tests/property_tests.rs +118 -0
- package/data/probe/tests/python_outline_format_tests.rs +1538 -0
- package/data/probe/tests/query_command_json_tests.rs +438 -0
- package/data/probe/tests/query_command_tests.rs +232 -0
- package/data/probe/tests/query_command_xml_tests.rs +569 -0
- package/data/probe/tests/quoted_term_with_negative_keyword_tests.rs +216 -0
- package/data/probe/tests/required_terms_filename_tests.rs +116 -0
- package/data/probe/tests/ruby_outline_format_tests.rs +1011 -0
- package/data/probe/tests/rust_line_comment_context_test.rs +151 -0
- package/data/probe/tests/rust_outline_format_enhanced_tests.rs +725 -0
- package/data/probe/tests/rust_outline_format_tests.rs +843 -0
- package/data/probe/tests/schemas/xml_output_schema.xsd +38 -0
- package/data/probe/tests/search_determinism_tests.rs +451 -0
- package/data/probe/tests/search_hints_tests.rs +253 -0
- package/data/probe/tests/special_character_escaping_tests.rs +417 -0
- package/data/probe/tests/stemming_compound_word_filtering_tests.rs +535 -0
- package/data/probe/tests/strict_elastic_syntax_tests.rs +404 -0
- package/data/probe/tests/swift_outline_format_tests.rs +3319 -0
- package/data/probe/tests/symbols_tests.rs +166 -0
- package/data/probe/tests/test_file.rs +45 -0
- package/data/probe/tests/test_tokenize.rs +28 -0
- package/data/probe/tests/timeout_tests.rs +82 -0
- package/data/probe/tests/tokenization_tests.rs +195 -0
- package/data/probe/tests/tokenized_block_filtering_tests.rs +174 -0
- package/data/probe/tests/typescript_extract_tests.rs +214 -0
- package/data/probe/tests/typescript_outline_format_tests.rs +2188 -0
- package/data/probe/tests/xml_format_tests.rs +568 -0
- package/data/probe/tests/xml_schema_validation_tests.rs +497 -0
- package/data/scripts/postinstall.mjs +9 -0
- package/data/scripts/set-version.js +0 -0
- package/data/scripts/wiki-build.sh +111 -0
- package/data/scripts/wiki-deploy.sh +73 -0
- package/data/serve.json +12 -0
- package/data/test/demo-dynamic.html +134 -0
- package/data/test/demo-esm.html +105 -0
- package/data/test/demo-iife.html +78 -0
- package/data/tsconfig.json +7 -0
- package/data/vite.server.ts +483 -0
- package/data/vitest.config.ts +40 -0
- package/data/wiki/Home.md +58 -0
- package/data/wiki/_Sidebar.md +39 -0
- package/docs-mcp.config.json +20 -0
- package/package.json +56 -0
- package/src/config.js +111 -0
- package/src/index.js +395 -0
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aider backend implementation for code implementation tasks
|
|
3
|
+
* @module AiderBackend
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import BaseBackend from './BaseBackend.js';
|
|
7
|
+
import { BackendError, ErrorTypes, ProgressTracker, FileChangeParser, TokenEstimator } from '../core/utils.js';
|
|
8
|
+
import { spawn, exec } from 'child_process';
|
|
9
|
+
import { promisify } from 'util';
|
|
10
|
+
import { promises as fsPromises } from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import os from 'os';
|
|
13
|
+
import { TIMEOUTS, getDefaultTimeoutMs } from '../core/timeouts.js';
|
|
14
|
+
|
|
15
|
+
const execPromise = promisify(exec);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Aider implementation backend
|
|
19
|
+
* @class
|
|
20
|
+
* @extends BaseBackend
|
|
21
|
+
*/
|
|
22
|
+
class AiderBackend extends BaseBackend {
|
|
23
|
+
constructor() {
|
|
24
|
+
super('aider', '1.0.0');
|
|
25
|
+
this.config = null;
|
|
26
|
+
this.aiderVersion = null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @override
|
|
31
|
+
*/
|
|
32
|
+
async initialize(config) {
|
|
33
|
+
this.config = {
|
|
34
|
+
command: 'aider',
|
|
35
|
+
timeout: getDefaultTimeoutMs(), // Use centralized default (20 minutes)
|
|
36
|
+
maxOutputSize: 10 * 1024 * 1024, // 10MB
|
|
37
|
+
additionalArgs: [],
|
|
38
|
+
environment: {},
|
|
39
|
+
autoCommit: false,
|
|
40
|
+
modelSelection: 'auto',
|
|
41
|
+
...config
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Test aider availability
|
|
45
|
+
const available = await this.isAvailable();
|
|
46
|
+
if (!available) {
|
|
47
|
+
throw new BackendError(
|
|
48
|
+
'Aider command not found or not accessible. Please install aider with: pip install aider-chat',
|
|
49
|
+
ErrorTypes.DEPENDENCY_MISSING,
|
|
50
|
+
'AIDER_NOT_FOUND'
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Get aider version
|
|
55
|
+
try {
|
|
56
|
+
const { stdout } = await execPromise('aider --version', { timeout: TIMEOUTS.VERSION_CHECK });
|
|
57
|
+
this.aiderVersion = stdout.trim();
|
|
58
|
+
this.log('info', `Initialized with aider version: ${this.aiderVersion}`);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
this.log('warn', 'Could not determine aider version', { error: error.message });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.initialized = true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @override
|
|
68
|
+
*/
|
|
69
|
+
async isAvailable() {
|
|
70
|
+
try {
|
|
71
|
+
// Test if aider command exists
|
|
72
|
+
await execPromise('which aider', { timeout: TIMEOUTS.VERSION_CHECK });
|
|
73
|
+
|
|
74
|
+
// Check if API key is available
|
|
75
|
+
const hasApiKey = !!(
|
|
76
|
+
process.env.ANTHROPIC_API_KEY ||
|
|
77
|
+
process.env.OPENAI_API_KEY ||
|
|
78
|
+
process.env.GOOGLE_API_KEY ||
|
|
79
|
+
process.env.GEMINI_API_KEY
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
if (!hasApiKey) {
|
|
83
|
+
this.log('warn', 'No API key found. Aider requires ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY');
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return true;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @override
|
|
95
|
+
*/
|
|
96
|
+
getRequiredDependencies() {
|
|
97
|
+
return [
|
|
98
|
+
{
|
|
99
|
+
name: 'aider-chat',
|
|
100
|
+
type: 'pip',
|
|
101
|
+
version: '>=0.20.0',
|
|
102
|
+
installCommand: 'pip install aider-chat',
|
|
103
|
+
description: 'AI pair programming tool'
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'API Key',
|
|
107
|
+
type: 'environment',
|
|
108
|
+
description: 'One of: ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEY, or GEMINI_API_KEY'
|
|
109
|
+
}
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* @override
|
|
115
|
+
*/
|
|
116
|
+
getCapabilities() {
|
|
117
|
+
return {
|
|
118
|
+
supportsLanguages: ['python', 'javascript', 'typescript', 'go', 'rust', 'java', 'cpp', 'c', 'csharp', 'ruby', 'php', 'swift'],
|
|
119
|
+
supportsStreaming: true,
|
|
120
|
+
supportsRollback: true,
|
|
121
|
+
supportsDirectFileEdit: true,
|
|
122
|
+
supportsPlanGeneration: false,
|
|
123
|
+
supportsTestGeneration: false,
|
|
124
|
+
maxConcurrentSessions: 3
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @override
|
|
130
|
+
*/
|
|
131
|
+
getDescription() {
|
|
132
|
+
return 'Aider - AI pair programming in your terminal';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @override
|
|
137
|
+
*/
|
|
138
|
+
async execute(request) {
|
|
139
|
+
this.checkInitialized();
|
|
140
|
+
|
|
141
|
+
const validation = this.validateRequest(request);
|
|
142
|
+
if (!validation.valid) {
|
|
143
|
+
throw new BackendError(
|
|
144
|
+
`Invalid request: ${validation.errors.join(', ')}`,
|
|
145
|
+
ErrorTypes.VALIDATION_ERROR,
|
|
146
|
+
'INVALID_REQUEST'
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const sessionInfo = this.createSessionInfo(request.sessionId);
|
|
151
|
+
const progressTracker = new ProgressTracker(request.sessionId, request.callbacks?.onProgress);
|
|
152
|
+
|
|
153
|
+
this.activeSessions.set(request.sessionId, sessionInfo);
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
progressTracker.startStep('prepare', 'Preparing aider execution');
|
|
157
|
+
|
|
158
|
+
// Create temporary file for task
|
|
159
|
+
const tempDir = os.tmpdir();
|
|
160
|
+
const tempFileName = `aider-task-${request.sessionId}-${Date.now()}.txt`;
|
|
161
|
+
const tempFilePath = path.join(tempDir, tempFileName);
|
|
162
|
+
|
|
163
|
+
await fsPromises.writeFile(tempFilePath, request.task, 'utf8');
|
|
164
|
+
sessionInfo.tempFile = tempFilePath;
|
|
165
|
+
|
|
166
|
+
this.log('debug', 'Created temporary task file', { path: tempFilePath });
|
|
167
|
+
|
|
168
|
+
progressTracker.endStep();
|
|
169
|
+
progressTracker.startStep('execute', 'Executing aider');
|
|
170
|
+
|
|
171
|
+
// Validate working directory
|
|
172
|
+
const workingDir = this.validateWorkingDirectory(request.context?.workingDirectory || process.cwd());
|
|
173
|
+
|
|
174
|
+
this.updateSessionStatus(request.sessionId, {
|
|
175
|
+
status: 'running',
|
|
176
|
+
progress: 25,
|
|
177
|
+
message: 'Aider is processing your request'
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Execute aider
|
|
181
|
+
const result = await this.executeCommand(workingDir, request, sessionInfo, progressTracker);
|
|
182
|
+
|
|
183
|
+
progressTracker.endStep();
|
|
184
|
+
|
|
185
|
+
// Clean up temp file
|
|
186
|
+
try {
|
|
187
|
+
await fsPromises.unlink(tempFilePath);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
this.log('warn', 'Failed to clean up temp file', { path: tempFilePath, error: error.message });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this.updateSessionStatus(request.sessionId, {
|
|
193
|
+
status: 'completed',
|
|
194
|
+
progress: 100,
|
|
195
|
+
message: 'Implementation completed successfully'
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return result;
|
|
199
|
+
|
|
200
|
+
} catch (error) {
|
|
201
|
+
// Clean up temp file on error
|
|
202
|
+
if (sessionInfo.tempFile) {
|
|
203
|
+
try {
|
|
204
|
+
await fsPromises.unlink(sessionInfo.tempFile);
|
|
205
|
+
} catch (cleanupError) {
|
|
206
|
+
this.log('warn', 'Failed to clean up temp file on error', { error: cleanupError.message });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this.updateSessionStatus(request.sessionId, {
|
|
211
|
+
status: 'failed',
|
|
212
|
+
message: error.message
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
if (error instanceof BackendError) {
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
throw new BackendError(
|
|
220
|
+
`Aider execution failed: ${error.message}`,
|
|
221
|
+
ErrorTypes.EXECUTION_FAILED,
|
|
222
|
+
'AIDER_EXECUTION_FAILED',
|
|
223
|
+
{ originalError: error, sessionId: request.sessionId }
|
|
224
|
+
);
|
|
225
|
+
} finally {
|
|
226
|
+
this.activeSessions.delete(request.sessionId);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Build aider command arguments
|
|
232
|
+
* @param {import('../types/BackendTypes').ImplementRequest} request - Implementation request
|
|
233
|
+
* @param {string} tempFilePath - Path to temporary file with task
|
|
234
|
+
* @returns {Array<string>} Command arguments array for secure execution
|
|
235
|
+
* @private
|
|
236
|
+
*/
|
|
237
|
+
buildCommandArgs(request, tempFilePath) {
|
|
238
|
+
// Validate tempFilePath to prevent injection
|
|
239
|
+
if (!tempFilePath || typeof tempFilePath !== 'string') {
|
|
240
|
+
throw new BackendError(
|
|
241
|
+
'Invalid temporary file path',
|
|
242
|
+
ErrorTypes.VALIDATION_ERROR,
|
|
243
|
+
'INVALID_TEMP_FILE_PATH'
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const args = [
|
|
248
|
+
'--yes',
|
|
249
|
+
'--no-check-update',
|
|
250
|
+
'--no-analytics',
|
|
251
|
+
'--message-file',
|
|
252
|
+
tempFilePath // Separate argument to prevent injection
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
// Handle auto-commit option
|
|
256
|
+
if (!request.options?.autoCommit && !this.config.autoCommit) {
|
|
257
|
+
args.push('--no-auto-commits');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Add model selection
|
|
261
|
+
const model = this.selectModel(request);
|
|
262
|
+
if (model) {
|
|
263
|
+
// Validate model name to prevent injection
|
|
264
|
+
if (this.isValidModelName(model)) {
|
|
265
|
+
args.push('--model');
|
|
266
|
+
args.push(model);
|
|
267
|
+
} else {
|
|
268
|
+
this.log('warn', `Invalid model name ignored: ${model}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Add timeout if specified
|
|
273
|
+
if (request.options?.timeout || this.config.timeout) {
|
|
274
|
+
const timeoutSeconds = Math.floor((request.options?.timeout || this.config.timeout) / 1000);
|
|
275
|
+
// Note: aider doesn't have a built-in timeout, this would need to be handled at process level
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Add additional arguments from config with validation
|
|
279
|
+
if (this.config.additionalArgs && this.config.additionalArgs.length > 0) {
|
|
280
|
+
const validatedArgs = this.validateAdditionalArgs(this.config.additionalArgs);
|
|
281
|
+
args.push(...validatedArgs);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Add any custom arguments from request with validation
|
|
285
|
+
if (request.options?.additionalArgs) {
|
|
286
|
+
const validatedArgs = this.validateAdditionalArgs(request.options.additionalArgs);
|
|
287
|
+
args.push(...validatedArgs);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return args;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Select the appropriate model based on configuration and environment
|
|
295
|
+
* @param {import('../types/BackendTypes').ImplementRequest} request - Implementation request
|
|
296
|
+
* @returns {string|null} Model identifier or null
|
|
297
|
+
* @private
|
|
298
|
+
*/
|
|
299
|
+
selectModel(request) {
|
|
300
|
+
// Priority: request option > config > environment-based auto-selection
|
|
301
|
+
if (request.options?.model) {
|
|
302
|
+
return request.options.model;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (this.config.model) {
|
|
306
|
+
return this.config.model;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (this.config.modelSelection === 'auto') {
|
|
310
|
+
// Auto-select based on available API keys
|
|
311
|
+
const geminiApiKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
|
|
312
|
+
const anthropicApiKey = process.env.ANTHROPIC_API_KEY;
|
|
313
|
+
const openaiApiKey = process.env.OPENAI_API_KEY;
|
|
314
|
+
|
|
315
|
+
if (geminiApiKey) {
|
|
316
|
+
return 'gemini/gemini-2.5-pro';
|
|
317
|
+
} else if (anthropicApiKey) {
|
|
318
|
+
return 'claude-3-5-sonnet-20241022';
|
|
319
|
+
} else if (openaiApiKey) {
|
|
320
|
+
return 'gpt-4';
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Validate model name to prevent command injection
|
|
329
|
+
* @param {string} model - Model name to validate
|
|
330
|
+
* @returns {boolean} True if valid, false otherwise
|
|
331
|
+
* @private
|
|
332
|
+
*/
|
|
333
|
+
isValidModelName(model) {
|
|
334
|
+
// Just check if it's a non-empty string
|
|
335
|
+
// Model names change frequently and formats vary
|
|
336
|
+
return model && typeof model === 'string' && model.trim().length > 0;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Validate additional arguments to prevent command injection
|
|
341
|
+
* @param {Array<string>} args - Arguments to validate
|
|
342
|
+
* @returns {Array<string>} Validated arguments
|
|
343
|
+
* @private
|
|
344
|
+
*/
|
|
345
|
+
validateAdditionalArgs(args) {
|
|
346
|
+
if (!Array.isArray(args)) {
|
|
347
|
+
this.log('warn', 'additionalArgs must be an array, ignoring');
|
|
348
|
+
return [];
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const validatedArgs = [];
|
|
352
|
+
const maxArgLength = 500; // Reasonable limit for individual arguments
|
|
353
|
+
|
|
354
|
+
for (const arg of args) {
|
|
355
|
+
if (typeof arg !== 'string') {
|
|
356
|
+
this.log('warn', `Skipping non-string argument: ${typeof arg}`);
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (arg.length > maxArgLength) {
|
|
361
|
+
this.log('warn', `Skipping overly long argument (${arg.length} chars)`);
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Check for dangerous patterns
|
|
366
|
+
if (this.containsShellMetacharacters(arg)) {
|
|
367
|
+
this.log('warn', `Skipping argument with shell metacharacters: ${arg.substring(0, 50)}`);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Validate common aider flags
|
|
372
|
+
if (this.isValidAiderArgument(arg)) {
|
|
373
|
+
validatedArgs.push(arg);
|
|
374
|
+
} else {
|
|
375
|
+
this.log('warn', `Skipping potentially unsafe argument: ${arg.substring(0, 50)}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return validatedArgs;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Check if string contains shell metacharacters
|
|
384
|
+
* @param {string} str - String to check
|
|
385
|
+
* @returns {boolean} True if contains metacharacters
|
|
386
|
+
* @private
|
|
387
|
+
*/
|
|
388
|
+
containsShellMetacharacters(str) {
|
|
389
|
+
// Common shell metacharacters that could be used for injection
|
|
390
|
+
const shellMetacharacters = /[;&|`$(){}[\]<>*?'"\\]/;
|
|
391
|
+
const controlChars = /[\x00-\x1f\x7f]/; // Control characters
|
|
392
|
+
|
|
393
|
+
return shellMetacharacters.test(str) || controlChars.test(str);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Validate if argument is a known safe aider argument
|
|
398
|
+
* @param {string} arg - Argument to validate
|
|
399
|
+
* @returns {boolean} True if valid aider argument
|
|
400
|
+
* @private
|
|
401
|
+
*/
|
|
402
|
+
isValidAiderArgument(arg) {
|
|
403
|
+
// Whitelist of known safe aider arguments
|
|
404
|
+
const safeAiderFlags = [
|
|
405
|
+
'--yes', '--no-check-update', '--no-analytics', '--no-auto-commits',
|
|
406
|
+
'--model', '--message-file', '--dry-run', '--map-tokens', '--show-model-warnings',
|
|
407
|
+
'--no-show-model-warnings', '--edit-format', '--architect', '--weak-model',
|
|
408
|
+
'--cache-prompts', '--no-cache-prompts', '--map-refresh', '--restore-chat-history',
|
|
409
|
+
'--encoding', '--config'
|
|
410
|
+
];
|
|
411
|
+
|
|
412
|
+
// Check if it's a known flag
|
|
413
|
+
if (safeAiderFlags.includes(arg)) {
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Check if it's a flag with equals sign (like --model=value)
|
|
418
|
+
for (const flag of safeAiderFlags) {
|
|
419
|
+
if (arg.startsWith(flag + '=')) {
|
|
420
|
+
const value = arg.substring(flag.length + 1);
|
|
421
|
+
return !this.containsShellMetacharacters(value) && value.length <= 100;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Allow simple values that don't look like flags if they're safe
|
|
426
|
+
if (!arg.startsWith('-') && !this.containsShellMetacharacters(arg) && arg.length <= 100) {
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Validate command path to prevent command injection
|
|
435
|
+
* @param {string} command - Command to validate
|
|
436
|
+
* @returns {boolean} True if valid
|
|
437
|
+
* @private
|
|
438
|
+
*/
|
|
439
|
+
isValidCommand(command) {
|
|
440
|
+
if (!command || typeof command !== 'string') {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Only allow alphanumeric, hyphens, underscores, and forward slashes for paths
|
|
445
|
+
const validCommandPattern = /^[a-zA-Z0-9._/-]+$/;
|
|
446
|
+
const maxLength = 200; // Reasonable limit for command paths
|
|
447
|
+
|
|
448
|
+
return validCommandPattern.test(command) &&
|
|
449
|
+
command.length <= maxLength &&
|
|
450
|
+
!this.containsShellMetacharacters(command);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Validate working directory path
|
|
455
|
+
* @param {string} dir - Directory path to validate
|
|
456
|
+
* @returns {string} Validated directory path
|
|
457
|
+
* @private
|
|
458
|
+
*/
|
|
459
|
+
validateWorkingDirectory(dir) {
|
|
460
|
+
if (!dir || typeof dir !== 'string') {
|
|
461
|
+
throw new BackendError(
|
|
462
|
+
'Invalid working directory',
|
|
463
|
+
ErrorTypes.VALIDATION_ERROR,
|
|
464
|
+
'INVALID_WORKING_DIRECTORY'
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Resolve path to prevent directory traversal
|
|
469
|
+
const resolvedPath = path.resolve(dir);
|
|
470
|
+
|
|
471
|
+
// Basic validation - ensure it doesn't contain obvious injection attempts
|
|
472
|
+
if (this.containsShellMetacharacters(resolvedPath)) {
|
|
473
|
+
throw new BackendError(
|
|
474
|
+
'Working directory contains unsafe characters',
|
|
475
|
+
ErrorTypes.VALIDATION_ERROR,
|
|
476
|
+
'UNSAFE_WORKING_DIRECTORY'
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return resolvedPath;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Validate environment variables
|
|
485
|
+
* @param {Object} env - Environment variables to validate
|
|
486
|
+
* @returns {Object} Validated environment variables
|
|
487
|
+
* @private
|
|
488
|
+
*/
|
|
489
|
+
validateEnvironment(env) {
|
|
490
|
+
if (!env || typeof env !== 'object') {
|
|
491
|
+
return {};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const validatedEnv = {};
|
|
495
|
+
const maxValueLength = 1000; // Reasonable limit for env values
|
|
496
|
+
|
|
497
|
+
for (const [key, value] of Object.entries(env)) {
|
|
498
|
+
// Validate key
|
|
499
|
+
if (typeof key !== 'string' || !/^[A-Z_][A-Z0-9_]*$/i.test(key)) {
|
|
500
|
+
this.log('warn', `Skipping invalid environment variable key: ${key}`);
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Validate value
|
|
505
|
+
if (typeof value !== 'string') {
|
|
506
|
+
this.log('warn', `Skipping non-string environment variable value for: ${key}`);
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (value.length > maxValueLength) {
|
|
511
|
+
this.log('warn', `Skipping overly long environment variable value for: ${key}`);
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Don't allow control characters in environment variables
|
|
516
|
+
if (/[\x00-\x1f\x7f]/.test(value)) {
|
|
517
|
+
this.log('warn', `Skipping environment variable with control characters: ${key}`);
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
validatedEnv[key] = value;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return validatedEnv;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Execute aider command
|
|
529
|
+
* @param {string} workingDir - Working directory
|
|
530
|
+
* @param {import('../types/BackendTypes').ImplementRequest} request - Implementation request
|
|
531
|
+
* @param {Object} sessionInfo - Session information
|
|
532
|
+
* @param {ProgressTracker} progressTracker - Progress tracker
|
|
533
|
+
* @returns {Promise<import('../types/BackendTypes').ImplementResult>}
|
|
534
|
+
* @private
|
|
535
|
+
*/
|
|
536
|
+
async executeCommand(workingDir, request, sessionInfo, progressTracker) {
|
|
537
|
+
return new Promise((resolve, reject) => {
|
|
538
|
+
const startTime = Date.now();
|
|
539
|
+
|
|
540
|
+
// Build command arguments securely
|
|
541
|
+
const commandArgs = this.buildCommandArgs(request, sessionInfo.tempFile);
|
|
542
|
+
const commandPath = this.config.command || 'aider';
|
|
543
|
+
|
|
544
|
+
this.log('info', 'Executing aider command', {
|
|
545
|
+
command: commandPath,
|
|
546
|
+
args: commandArgs.slice(0, 5), // Log first few args only for security
|
|
547
|
+
workingDir
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// Validate command exists and is safe
|
|
551
|
+
if (!this.isValidCommand(commandPath)) {
|
|
552
|
+
throw new BackendError(
|
|
553
|
+
'Invalid or unsafe command path',
|
|
554
|
+
ErrorTypes.VALIDATION_ERROR,
|
|
555
|
+
'INVALID_COMMAND_PATH'
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Spawn the process directly (no shell interpretation)
|
|
560
|
+
const childProcess = spawn(commandPath, commandArgs, {
|
|
561
|
+
cwd: workingDir,
|
|
562
|
+
env: { ...process.env, ...this.validateEnvironment(this.config.environment) }
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
sessionInfo.childProcess = childProcess;
|
|
566
|
+
sessionInfo.cancel = () => {
|
|
567
|
+
if (childProcess && !childProcess.killed) {
|
|
568
|
+
this.log('info', 'Cancelling aider process', { sessionId: request.sessionId });
|
|
569
|
+
childProcess.kill('SIGTERM');
|
|
570
|
+
setTimeout(() => {
|
|
571
|
+
if (!childProcess.killed) {
|
|
572
|
+
childProcess.kill('SIGKILL');
|
|
573
|
+
}
|
|
574
|
+
}, 5000);
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
let stdoutData = '';
|
|
579
|
+
let stderrData = '';
|
|
580
|
+
let outputSize = 0;
|
|
581
|
+
let lastProgressUpdate = Date.now();
|
|
582
|
+
|
|
583
|
+
// Handle stdout
|
|
584
|
+
childProcess.stdout.on('data', (data) => {
|
|
585
|
+
const output = data.toString();
|
|
586
|
+
outputSize += output.length;
|
|
587
|
+
|
|
588
|
+
// Check output size limit
|
|
589
|
+
if (outputSize > this.config.maxOutputSize) {
|
|
590
|
+
childProcess.kill('SIGTERM');
|
|
591
|
+
reject(new BackendError(
|
|
592
|
+
'Output size exceeded maximum limit',
|
|
593
|
+
ErrorTypes.EXECUTION_FAILED,
|
|
594
|
+
'OUTPUT_TOO_LARGE',
|
|
595
|
+
{ limit: this.config.maxOutputSize, actual: outputSize }
|
|
596
|
+
));
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
stdoutData += output;
|
|
601
|
+
|
|
602
|
+
// Stream output to stderr for real-time visibility
|
|
603
|
+
process.stderr.write(output);
|
|
604
|
+
|
|
605
|
+
// Send progress updates (throttled)
|
|
606
|
+
const now = Date.now();
|
|
607
|
+
if (now - lastProgressUpdate > 1000) { // Update every second
|
|
608
|
+
progressTracker.reportMessage(output.trim(), 'stdout');
|
|
609
|
+
lastProgressUpdate = now;
|
|
610
|
+
|
|
611
|
+
// Update session progress
|
|
612
|
+
const elapsedSeconds = Math.floor((now - startTime) / 1000);
|
|
613
|
+
const estimatedProgress = Math.min(25 + (elapsedSeconds * 2), 90); // Cap at 90%
|
|
614
|
+
this.updateSessionStatus(request.sessionId, {
|
|
615
|
+
progress: estimatedProgress
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
// Handle stderr
|
|
621
|
+
childProcess.stderr.on('data', (data) => {
|
|
622
|
+
const output = data.toString();
|
|
623
|
+
stderrData += output;
|
|
624
|
+
|
|
625
|
+
// Stream to stderr
|
|
626
|
+
process.stderr.write(output);
|
|
627
|
+
|
|
628
|
+
// Report warnings
|
|
629
|
+
if (output.toLowerCase().includes('warning') || output.toLowerCase().includes('error')) {
|
|
630
|
+
progressTracker.reportMessage(output.trim(), 'stderr');
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
// Handle process completion
|
|
635
|
+
childProcess.on('close', (code) => {
|
|
636
|
+
const executionTime = Date.now() - startTime;
|
|
637
|
+
|
|
638
|
+
// Clear timeout
|
|
639
|
+
clearTimeout(timeoutId);
|
|
640
|
+
|
|
641
|
+
this.log('info', `Aider process exited`, {
|
|
642
|
+
code,
|
|
643
|
+
executionTime,
|
|
644
|
+
outputSize: stdoutData.length
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
// Parse file changes from output
|
|
648
|
+
const changes = FileChangeParser.parseChanges(stdoutData + stderrData, workingDir);
|
|
649
|
+
const diffStats = FileChangeParser.extractDiffStats(stdoutData + stderrData);
|
|
650
|
+
|
|
651
|
+
if (code === 0) {
|
|
652
|
+
// Check for errors in output even if exit code is 0
|
|
653
|
+
const combinedOutput = stdoutData + stderrData;
|
|
654
|
+
const hasAuthError = /AuthenticationError|Invalid API key|insufficient permissions|not able to authenticate/i.test(combinedOutput);
|
|
655
|
+
const hasOtherErrors = /Error:|Exception:|Failed:|fatal:/i.test(combinedOutput);
|
|
656
|
+
const hasChanges = changes.length > 0;
|
|
657
|
+
|
|
658
|
+
// Only consider it successful if:
|
|
659
|
+
// 1. No authentication/critical errors found in output
|
|
660
|
+
// 2. Either changes were made OR this was an informational command
|
|
661
|
+
const isActualSuccess = !hasAuthError && !hasOtherErrors && (hasChanges || !request.requiresChanges);
|
|
662
|
+
|
|
663
|
+
if (isActualSuccess) {
|
|
664
|
+
resolve({
|
|
665
|
+
success: true,
|
|
666
|
+
sessionId: request.sessionId,
|
|
667
|
+
output: stdoutData,
|
|
668
|
+
changes,
|
|
669
|
+
metrics: {
|
|
670
|
+
executionTime,
|
|
671
|
+
filesModified: changes.length,
|
|
672
|
+
linesChanged: diffStats.insertions + diffStats.deletions,
|
|
673
|
+
tokensUsed: TokenEstimator.estimate(request.task + stdoutData),
|
|
674
|
+
exitCode: code
|
|
675
|
+
},
|
|
676
|
+
metadata: {
|
|
677
|
+
command: commandPath,
|
|
678
|
+
args: commandArgs.slice(0, 5), // Limited args for security
|
|
679
|
+
workingDirectory: workingDir,
|
|
680
|
+
aiderVersion: this.aiderVersion
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
} else {
|
|
684
|
+
// Exit code 0 but actual failure detected
|
|
685
|
+
const errorType = hasAuthError ? 'AUTHENTICATION_ERROR' : 'EXECUTION_FAILED';
|
|
686
|
+
const errorMessage = hasAuthError
|
|
687
|
+
? 'Authentication failed - check API key and permissions'
|
|
688
|
+
: `Aider completed but encountered errors: ${combinedOutput.substring(0, 200)}...`;
|
|
689
|
+
|
|
690
|
+
reject(new BackendError(
|
|
691
|
+
errorMessage,
|
|
692
|
+
hasAuthError ? ErrorTypes.AUTHENTICATION : ErrorTypes.EXECUTION_FAILED,
|
|
693
|
+
errorType,
|
|
694
|
+
{
|
|
695
|
+
exitCode: code,
|
|
696
|
+
hasChanges,
|
|
697
|
+
hasAuthError,
|
|
698
|
+
hasOtherErrors,
|
|
699
|
+
stdout: stdoutData.substring(0, 1000),
|
|
700
|
+
stderr: stderrData.substring(0, 1000)
|
|
701
|
+
}
|
|
702
|
+
));
|
|
703
|
+
}
|
|
704
|
+
} else {
|
|
705
|
+
reject(new BackendError(
|
|
706
|
+
`Aider process exited with code ${code}`,
|
|
707
|
+
ErrorTypes.EXECUTION_FAILED,
|
|
708
|
+
'AIDER_PROCESS_FAILED',
|
|
709
|
+
{
|
|
710
|
+
exitCode: code,
|
|
711
|
+
stdout: stdoutData.substring(0, 1000),
|
|
712
|
+
stderr: stderrData.substring(0, 1000)
|
|
713
|
+
}
|
|
714
|
+
));
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
// Handle process errors
|
|
719
|
+
childProcess.on('error', (error) => {
|
|
720
|
+
// Clear timeout
|
|
721
|
+
clearTimeout(timeoutId);
|
|
722
|
+
|
|
723
|
+
this.log('error', 'Failed to spawn aider process', { error: error.message });
|
|
724
|
+
reject(new BackendError(
|
|
725
|
+
`Failed to spawn aider process: ${error.message}`,
|
|
726
|
+
ErrorTypes.EXECUTION_FAILED,
|
|
727
|
+
'AIDER_SPAWN_FAILED',
|
|
728
|
+
{ originalError: error }
|
|
729
|
+
));
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
// Set timeout
|
|
733
|
+
const timeout = request.options?.timeout || this.config.timeout;
|
|
734
|
+
const timeoutId = setTimeout(() => {
|
|
735
|
+
if (!childProcess.killed) {
|
|
736
|
+
this.log('warn', 'Aider execution timed out', { timeout });
|
|
737
|
+
childProcess.kill('SIGTERM');
|
|
738
|
+
reject(new BackendError(
|
|
739
|
+
`Aider execution timed out after ${timeout}ms`,
|
|
740
|
+
ErrorTypes.TIMEOUT,
|
|
741
|
+
'AIDER_TIMEOUT',
|
|
742
|
+
{ timeout }
|
|
743
|
+
));
|
|
744
|
+
}
|
|
745
|
+
}, timeout);
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
export default AiderBackend;
|