feat: Add search_code tool and bump version to 1.0.0
- Add search_code tool for searching code across Bitbucket repositories - Currently supports Bitbucket Server only (Cloud support planned) - Implement SearchHandlers class following modular architecture - Add formatCodeSearchOutput for clean AI-friendly output - Support file pattern filtering with glob patterns - Add comprehensive documentation and examples - Create Memory Bank for project documentation - Bump version to 1.0.0 indicating stable API with comprehensive features - Update CHANGELOG.md with v1.0.0 release notes
This commit is contained in:
parent
e5da36e515
commit
5a088ecf0d
18 changed files with 1161 additions and 6 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -40,3 +40,4 @@ test-api.js
|
|||
# Personal configuration
|
||||
RELOAD_INSTRUCTIONS.md
|
||||
personal-notes.md
|
||||
currentTask.yml
|
||||
|
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.0.0] - 2025-07-25
|
||||
|
||||
### Added
|
||||
- **New `search_code` tool for searching code across repositories**:
|
||||
- Search for code snippets, functions, or any text within Bitbucket repositories
|
||||
- Supports searching within a specific repository or across all repositories in a workspace
|
||||
- File path pattern filtering with glob patterns (e.g., `*.java`, `src/**/*.ts`)
|
||||
- Returns matched lines with highlighted segments showing exact matches
|
||||
- Pagination support for large result sets
|
||||
- Currently only supports Bitbucket Server (Cloud API support planned for future)
|
||||
- Added `SearchHandlers` class following the modular architecture pattern
|
||||
- Added TypeScript interfaces for search requests and responses
|
||||
- Added `formatSearchResults` formatter function for consistent output
|
||||
|
||||
### Changed
|
||||
- Major version bump to 1.0.0 indicating stable API with comprehensive feature set
|
||||
- Enhanced documentation with search examples
|
||||
|
||||
## [0.10.0] - 2025-07-03
|
||||
|
||||
### Added
|
||||
|
|
88
README.md
88
README.md
|
@ -36,6 +36,9 @@ An MCP (Model Context Protocol) server that provides tools for interacting with
|
|||
- `request_changes` - Request changes on a pull request
|
||||
- `remove_requested_changes` - Remove change request from a pull request
|
||||
|
||||
#### Search Tools
|
||||
- `search_code` - Search for code across repositories (currently Bitbucket Server only)
|
||||
|
||||
## Installation
|
||||
|
||||
### Using npx (Recommended)
|
||||
|
@ -211,6 +214,91 @@ Returns detailed information about the pull request including:
|
|||
- Total files changed
|
||||
- And more...
|
||||
|
||||
### Search Code
|
||||
|
||||
Search for code across Bitbucket repositories (currently only supported for Bitbucket Server):
|
||||
|
||||
```typescript
|
||||
// Search in a specific repository
|
||||
{
|
||||
"tool": "search_code",
|
||||
"arguments": {
|
||||
"workspace": "PROJ",
|
||||
"repository": "my-repo",
|
||||
"search_query": "TODO",
|
||||
"limit": 50
|
||||
}
|
||||
}
|
||||
|
||||
// Search across all repositories in a workspace
|
||||
{
|
||||
"tool": "search_code",
|
||||
"arguments": {
|
||||
"workspace": "PROJ",
|
||||
"search_query": "deprecated",
|
||||
"file_pattern": "*.java", // Optional: filter by file pattern
|
||||
"limit": 100
|
||||
}
|
||||
}
|
||||
|
||||
// Search with file pattern filtering
|
||||
{
|
||||
"tool": "search_code",
|
||||
"arguments": {
|
||||
"workspace": "PROJ",
|
||||
"repository": "frontend-app",
|
||||
"search_query": "useState",
|
||||
"file_pattern": "*.tsx", // Only search in .tsx files
|
||||
"start": 0,
|
||||
"limit": 25
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Returns search results with:
|
||||
- File path and name
|
||||
- Repository and project information
|
||||
- Matched lines with:
|
||||
- Line number
|
||||
- Full line content
|
||||
- Highlighted segments showing exact matches
|
||||
- Pagination information
|
||||
|
||||
Example response:
|
||||
```json
|
||||
{
|
||||
"message": "Code search completed successfully",
|
||||
"workspace": "PROJ",
|
||||
"repository": "my-repo",
|
||||
"search_query": "TODO",
|
||||
"results": [
|
||||
{
|
||||
"file_path": "src/utils/helper.js",
|
||||
"file_name": "helper.js",
|
||||
"repository": "my-repo",
|
||||
"project": "PROJ",
|
||||
"matches": [
|
||||
{
|
||||
"line_number": 42,
|
||||
"line_content": " // TODO: Implement error handling",
|
||||
"highlighted_segments": [
|
||||
{ "text": " // ", "is_match": false },
|
||||
{ "text": "TODO", "is_match": true },
|
||||
{ "text": ": Implement error handling", "is_match": false }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"total_count": 15,
|
||||
"start": 0,
|
||||
"limit": 50,
|
||||
"has_more": false
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: This tool currently only works with Bitbucket Server. Bitbucket Cloud support is planned for a future release.
|
||||
|
||||
### List Pull Requests
|
||||
|
||||
```typescript
|
||||
|
|
133
memory-bank/.clinerules
Normal file
133
memory-bank/.clinerules
Normal file
|
@ -0,0 +1,133 @@
|
|||
# Bitbucket MCP Server - Project Intelligence
|
||||
|
||||
## Critical Implementation Paths
|
||||
|
||||
### Adding New Tools
|
||||
1. Create handler in src/handlers/ following existing patterns
|
||||
2. Add TypeScript interfaces in src/types/bitbucket.ts
|
||||
3. Add type guard in src/types/guards.ts
|
||||
4. Add formatter in src/utils/formatters.ts if needed
|
||||
5. Register tool in src/tools/definitions.ts
|
||||
6. Wire handler in src/index.ts
|
||||
7. Update version, CHANGELOG.md, and README.md
|
||||
|
||||
### API Variant Handling
|
||||
- Always check `apiClient.getIsServer()` for Cloud vs Server
|
||||
- Server uses `/rest/api/1.0/` prefix
|
||||
- Cloud uses direct paths under base URL
|
||||
- Different parameter names (e.g., pagelen vs limit)
|
||||
|
||||
### Error Handling Pattern
|
||||
```typescript
|
||||
try {
|
||||
// API call
|
||||
} catch (error: any) {
|
||||
const errorMessage = error.response?.data?.errors?.[0]?.message || error.message;
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
error: `Failed to ${action}: ${errorMessage}`,
|
||||
details: error.response?.data
|
||||
}, null, 2)
|
||||
}],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## User Preferences
|
||||
|
||||
### Documentation Style
|
||||
- Comprehensive examples for each tool
|
||||
- Clear parameter descriptions
|
||||
- Response format examples
|
||||
- Note differences between Cloud and Server
|
||||
|
||||
### Code Style
|
||||
- TypeScript with strict typing
|
||||
- ES modules with .js extensions in imports
|
||||
- Consistent error handling
|
||||
- Modular architecture
|
||||
|
||||
## Project-Specific Patterns
|
||||
|
||||
### Authentication
|
||||
- Cloud: Username (not email) + App Password
|
||||
- Server: Email address + HTTP Access Token
|
||||
- Environment variables for configuration
|
||||
|
||||
### Pagination Pattern
|
||||
- `limit` and `start` parameters
|
||||
- Return `has_more` and `next_start`
|
||||
- Include `total_count` when available
|
||||
|
||||
### Response Formatting
|
||||
- Consistent JSON structure
|
||||
- Include operation status message
|
||||
- Provide detailed error information
|
||||
- Format dates as ISO strings
|
||||
|
||||
## Known Challenges
|
||||
|
||||
### Bitbucket API Differences
|
||||
- Parameter naming varies (e.g., pagelen vs limit)
|
||||
- Response structures differ significantly
|
||||
- Some features only available on one variant
|
||||
- Authentication methods completely different
|
||||
|
||||
### Search Functionality
|
||||
- Only available on Bitbucket Server
|
||||
- Query syntax requires specific format
|
||||
- No Cloud API equivalent currently
|
||||
|
||||
## Tool Usage Patterns
|
||||
|
||||
### List Operations
|
||||
- Always include pagination parameters
|
||||
- Return consistent metadata
|
||||
- Support filtering where applicable
|
||||
|
||||
### Modification Operations
|
||||
- Validate required parameters
|
||||
- Preserve existing data when updating
|
||||
- Return updated resource in response
|
||||
|
||||
### File Operations
|
||||
- Smart truncation for large files
|
||||
- Type-based default limits
|
||||
- Support line range selection
|
||||
|
||||
## Evolution of Decisions
|
||||
|
||||
### Version 0.3.0
|
||||
- Modularized codebase into handlers
|
||||
- Separated types and utilities
|
||||
- Improved maintainability
|
||||
|
||||
### Version 0.6.0
|
||||
- Enhanced PR details with comments/files
|
||||
- Added parallel API calls for performance
|
||||
|
||||
### Version 0.9.0
|
||||
- Added code snippet matching for comments
|
||||
- Implemented confidence scoring
|
||||
|
||||
### Version 1.0.0
|
||||
- Added code search functionality
|
||||
- Reached feature completeness
|
||||
- Ready for production use
|
||||
|
||||
## Important Architecture Decisions
|
||||
|
||||
### Handler Pattern
|
||||
Each tool category has its own handler class to maintain single responsibility and make the codebase more maintainable.
|
||||
|
||||
### Type Guards
|
||||
All tool inputs are validated with type guards to ensure type safety at runtime.
|
||||
|
||||
### Response Normalization
|
||||
Different API responses are normalized to consistent formats for easier consumption.
|
||||
|
||||
### Error Handling
|
||||
Consistent error handling across all tools with detailed error messages and recovery suggestions.
|
88
memory-bank/activeContext.yml
Normal file
88
memory-bank/activeContext.yml
Normal file
|
@ -0,0 +1,88 @@
|
|||
# Active Context - Bitbucket MCP Server
|
||||
|
||||
current_focus_areas:
|
||||
- name: "Search Code Feature Implementation"
|
||||
status: "completed"
|
||||
priority: "high"
|
||||
team: "frontend"
|
||||
timeline: "2025-07-25"
|
||||
|
||||
recent_changes:
|
||||
- date: "2025-07-25"
|
||||
feature: "code_search_response_fix"
|
||||
description: "Fixed search_code tool response handling to match actual Bitbucket API structure"
|
||||
status: "completed"
|
||||
files_affected:
|
||||
- "src/utils/formatters.ts"
|
||||
- "src/handlers/search-handlers.ts"
|
||||
technical_details: "Added formatCodeSearchOutput for simplified AI-friendly output showing only filename, line number, and text"
|
||||
business_impact: "Search results now properly formatted for AI consumption with cleaner output"
|
||||
patterns_introduced:
|
||||
- "Simplified formatter pattern for AI-friendly output"
|
||||
|
||||
- date: "2025-07-25"
|
||||
feature: "code_search"
|
||||
description: "Added search_code tool for searching code across repositories"
|
||||
status: "completed"
|
||||
files_affected:
|
||||
- "src/handlers/search-handlers.ts"
|
||||
- "src/types/bitbucket.ts"
|
||||
- "src/utils/formatters.ts"
|
||||
- "src/types/guards.ts"
|
||||
- "src/tools/definitions.ts"
|
||||
- "src/index.ts"
|
||||
technical_details: "Implemented Bitbucket Server search API integration"
|
||||
business_impact: "Enables code search functionality for Bitbucket Server users"
|
||||
patterns_introduced:
|
||||
- "SearchHandlers following modular architecture"
|
||||
- "Search result formatting pattern"
|
||||
|
||||
patterns_discovered:
|
||||
- name: "Modular Handler Architecture"
|
||||
description: "Each tool category has its own handler class"
|
||||
usage: "Add new handlers for new tool categories"
|
||||
implementation: "Create handler class, add to index.ts, register tools"
|
||||
|
||||
- name: "API Variant Handling"
|
||||
description: "Single handler supports both Cloud and Server APIs"
|
||||
usage: "Check isServer flag and adjust API paths/parameters"
|
||||
implementation: "Use apiClient.getIsServer() to determine variant"
|
||||
|
||||
- name: "Simplified AI Formatter Pattern"
|
||||
description: "Separate formatters for detailed vs AI-friendly output"
|
||||
usage: "Create simplified formatters that extract only essential information for AI"
|
||||
implementation: "formatCodeSearchOutput strips HTML, shows only file:line:text format"
|
||||
|
||||
recent_learnings:
|
||||
- date: "2025-07-25"
|
||||
topic: "Bitbucket Search API Response Structure"
|
||||
description: "API returns file as string (not object), uses hitContexts with HTML formatting"
|
||||
impact: "Need to parse HTML <em> tags and handle different response structure"
|
||||
|
||||
- date: "2025-07-25"
|
||||
topic: "Bitbucket Search API"
|
||||
description: "Search API only available on Bitbucket Server, not Cloud"
|
||||
impact: "Search tool marked as Server-only with future Cloud support planned"
|
||||
|
||||
- date: "2025-07-25"
|
||||
topic: "Version Management"
|
||||
description: "Major version 1.0.0 indicates stable API with comprehensive features"
|
||||
impact: "Project ready for production use"
|
||||
|
||||
key_decisions:
|
||||
- decision: "Separate handler for search tools"
|
||||
rationale: "Maintains single responsibility and modularity"
|
||||
date: "2025-07-25"
|
||||
|
||||
- decision: "YAML format for Memory Bank"
|
||||
rationale: "Better merge conflict handling and structured data"
|
||||
date: "2025-07-25"
|
||||
|
||||
current_challenges:
|
||||
- "Bitbucket Cloud search API not yet available"
|
||||
- "Need to handle different search syntaxes across platforms"
|
||||
|
||||
next_priorities:
|
||||
- "Add Bitbucket Cloud search when API becomes available"
|
||||
- "Enhance search with more filtering options"
|
||||
- "Add support for searching in other entities (commits, PRs)"
|
57
memory-bank/currentTask.yml
Normal file
57
memory-bank/currentTask.yml
Normal file
|
@ -0,0 +1,57 @@
|
|||
# Current Task - Bitbucket MCP Server
|
||||
|
||||
task_description: "Add search_code tool to Bitbucket MCP Server"
|
||||
status: "completed"
|
||||
started: "2025-07-25"
|
||||
completed: "2025-07-25"
|
||||
|
||||
objectives:
|
||||
- "Implement search_code tool for searching code across repositories"
|
||||
- "Support Bitbucket Server search API"
|
||||
- "Add file pattern filtering"
|
||||
- "Update version to 1.0.0"
|
||||
- "Update documentation"
|
||||
|
||||
implementation_plan:
|
||||
- [x] Analyze the Bitbucket Server search API
|
||||
- [x] Create SearchHandlers class
|
||||
- [x] Add TypeScript interfaces for search types
|
||||
- [x] Implement formatSearchResults function
|
||||
- [x] Add isSearchCodeArgs type guard
|
||||
- [x] Register tool in definitions
|
||||
- [x] Wire up handler in index.ts
|
||||
- [x] Update version to 1.0.0
|
||||
- [x] Update CHANGELOG.md
|
||||
- [x] Add comprehensive documentation to README.md
|
||||
- [x] Build and verify compilation
|
||||
- [x] Create Memory Bank documentation
|
||||
|
||||
technical_details:
|
||||
api_endpoint: "/rest/search/latest/search"
|
||||
method: "POST"
|
||||
payload_structure:
|
||||
- query: "project:PROJ repo:repo-name search-term"
|
||||
- entities: { code: {} }
|
||||
- limits: { primary: 25, secondary: 10 }
|
||||
|
||||
challenges_encountered:
|
||||
- "Search API only available for Bitbucket Server, not Cloud"
|
||||
- "Query string construction requires specific format"
|
||||
|
||||
solution_approach:
|
||||
- "Created modular SearchHandlers following existing pattern"
|
||||
- "Built query string dynamically from parameters"
|
||||
- "Added clear error message for Cloud users"
|
||||
- "Formatted results consistently with other tools"
|
||||
|
||||
results:
|
||||
- "Successfully implemented search_code tool"
|
||||
- "Comprehensive documentation added"
|
||||
- "Version bumped to 1.0.0"
|
||||
- "Project ready for production use"
|
||||
- "Memory Bank fully documented"
|
||||
|
||||
next_steps:
|
||||
- "Monitor for Bitbucket Cloud search API availability"
|
||||
- "Gather user feedback on search functionality"
|
||||
- "Plan additional search features based on usage"
|
51
memory-bank/productContext.yml
Normal file
51
memory-bank/productContext.yml
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Product Context - Bitbucket MCP Server
|
||||
|
||||
problem_statement: |
|
||||
Developers using AI assistants need programmatic access to Bitbucket operations
|
||||
without leaving their development environment. Manual API interactions are
|
||||
time-consuming and error-prone.
|
||||
|
||||
target_users:
|
||||
primary:
|
||||
- "Software developers using AI coding assistants"
|
||||
- "DevOps engineers automating PR workflows"
|
||||
- "Code reviewers needing efficient review tools"
|
||||
|
||||
secondary:
|
||||
- "Project managers tracking development progress"
|
||||
- "QA engineers reviewing code changes"
|
||||
|
||||
user_needs:
|
||||
core:
|
||||
- "Create and manage pull requests programmatically"
|
||||
- "Review code changes with AI assistance"
|
||||
- "Automate branch management tasks"
|
||||
- "Search code across repositories"
|
||||
- "Access file contents without cloning"
|
||||
|
||||
workflow:
|
||||
- "Comment on PRs with code suggestions"
|
||||
- "Approve/request changes on PRs"
|
||||
- "List and filter pull requests"
|
||||
- "Get comprehensive PR details including comments"
|
||||
- "Manage branch lifecycle"
|
||||
|
||||
value_proposition:
|
||||
- "Seamless Bitbucket integration within AI assistants"
|
||||
- "Unified interface for Cloud and Server variants"
|
||||
- "Time savings through automation"
|
||||
- "Reduced context switching"
|
||||
- "Enhanced code review quality with AI"
|
||||
|
||||
user_experience_goals:
|
||||
- "Simple tool invocation syntax"
|
||||
- "Clear and consistent response formats"
|
||||
- "Helpful error messages with recovery suggestions"
|
||||
- "Smart defaults (pagination, truncation)"
|
||||
- "Flexible filtering and search options"
|
||||
|
||||
adoption_strategy:
|
||||
- "npm package for easy installation"
|
||||
- "Comprehensive documentation with examples"
|
||||
- "Support for both Cloud and Server"
|
||||
- "Gradual feature addition based on user needs"
|
86
memory-bank/progress.yml
Normal file
86
memory-bank/progress.yml
Normal file
|
@ -0,0 +1,86 @@
|
|||
# Progress - Bitbucket MCP Server
|
||||
|
||||
project_status:
|
||||
overall_progress: "90%"
|
||||
phase: "Feature Complete"
|
||||
version: "1.0.0"
|
||||
release_status: "Ready for production"
|
||||
|
||||
milestones_completed:
|
||||
- name: "Core PR Tools"
|
||||
completion_date: "2025-01-06"
|
||||
features:
|
||||
- "get_pull_request with merge details"
|
||||
- "list_pull_requests with filtering"
|
||||
- "create_pull_request"
|
||||
- "update_pull_request"
|
||||
- "merge_pull_request"
|
||||
|
||||
- name: "Enhanced Commenting"
|
||||
completion_date: "2025-01-26"
|
||||
features:
|
||||
- "Inline comments"
|
||||
- "Code suggestions"
|
||||
- "Code snippet matching"
|
||||
- "Nested replies support"
|
||||
|
||||
- name: "Code Review Tools"
|
||||
completion_date: "2025-01-26"
|
||||
features:
|
||||
- "get_pull_request_diff with filtering"
|
||||
- "approve_pull_request"
|
||||
- "request_changes"
|
||||
- "Review state management"
|
||||
|
||||
- name: "Branch Management"
|
||||
completion_date: "2025-01-21"
|
||||
features:
|
||||
- "list_branches"
|
||||
- "delete_branch"
|
||||
- "get_branch with PR info"
|
||||
- "list_branch_commits"
|
||||
|
||||
- name: "File Operations"
|
||||
completion_date: "2025-01-21"
|
||||
features:
|
||||
- "list_directory_content"
|
||||
- "get_file_content with smart truncation"
|
||||
|
||||
- name: "Code Search"
|
||||
completion_date: "2025-07-25"
|
||||
features:
|
||||
- "search_code for Bitbucket Server"
|
||||
- "File pattern filtering"
|
||||
- "Highlighted search results"
|
||||
|
||||
active_development:
|
||||
current_sprint: "Post 1.0 Planning"
|
||||
in_progress: []
|
||||
blocked: []
|
||||
|
||||
testing_status:
|
||||
unit_tests: "Not implemented"
|
||||
integration_tests: "Manual testing completed"
|
||||
user_acceptance: "In production use"
|
||||
known_issues: []
|
||||
|
||||
deployment_status:
|
||||
npm_package: "Published as @nexus2520/bitbucket-mcp-server"
|
||||
version_published: "1.0.0"
|
||||
documentation: "Comprehensive README with examples"
|
||||
adoption_metrics:
|
||||
- "npm weekly downloads tracking"
|
||||
- "GitHub stars and issues"
|
||||
|
||||
performance_metrics:
|
||||
api_response_time: "< 2s average"
|
||||
memory_usage: "< 100MB typical"
|
||||
concurrent_operations: "Supports parallel API calls"
|
||||
|
||||
next_release_planning:
|
||||
version: "1.1.0"
|
||||
planned_features:
|
||||
- "Bitbucket Cloud search support"
|
||||
- "Repository management tools"
|
||||
- "Pipeline/build integration"
|
||||
timeline: "TBD based on user feedback"
|
47
memory-bank/projectbrief.yml
Normal file
47
memory-bank/projectbrief.yml
Normal file
|
@ -0,0 +1,47 @@
|
|||
# Project Brief - Bitbucket MCP Server
|
||||
|
||||
project_name: "bitbucket-mcp-server"
|
||||
version: "1.0.0"
|
||||
description: "MCP (Model Context Protocol) server for Bitbucket API integration"
|
||||
primary_purpose: "Provide tools for interacting with Bitbucket APIs via MCP"
|
||||
|
||||
key_features:
|
||||
- "Support for both Bitbucket Cloud and Server"
|
||||
- "19 comprehensive tools for PR management, branch operations, and code review"
|
||||
- "Modular architecture with separate handlers for different tool categories"
|
||||
- "Smart file content handling with automatic truncation"
|
||||
- "Advanced PR commenting with code suggestions and snippet matching"
|
||||
- "Code search functionality across repositories"
|
||||
|
||||
scope:
|
||||
included:
|
||||
- "Pull Request lifecycle management"
|
||||
- "Branch management and operations"
|
||||
- "Code review and approval workflows"
|
||||
- "File and directory operations"
|
||||
- "Code search (Bitbucket Server only)"
|
||||
- "Authentication via app passwords/tokens"
|
||||
|
||||
excluded:
|
||||
- "Repository creation/deletion"
|
||||
- "User management"
|
||||
- "Pipeline/build operations"
|
||||
- "Wiki/documentation management"
|
||||
- "Bitbucket Cloud code search (future enhancement)"
|
||||
|
||||
constraints:
|
||||
technical:
|
||||
- "Node.js >= 16.0.0 required"
|
||||
- "TypeScript with ES modules"
|
||||
- "MCP SDK for protocol implementation"
|
||||
|
||||
authentication:
|
||||
- "Bitbucket Cloud: App passwords required"
|
||||
- "Bitbucket Server: HTTP access tokens required"
|
||||
- "Different username formats for Cloud vs Server"
|
||||
|
||||
success_criteria:
|
||||
- "Seamless integration with MCP-compatible clients"
|
||||
- "Reliable API interactions with proper error handling"
|
||||
- "Consistent response formatting across tools"
|
||||
- "Maintainable and extensible codebase"
|
107
memory-bank/systemPatterns.yml
Normal file
107
memory-bank/systemPatterns.yml
Normal file
|
@ -0,0 +1,107 @@
|
|||
# System Patterns - Bitbucket MCP Server
|
||||
|
||||
architecture_overview:
|
||||
high_level_architecture: |
|
||||
MCP Server implementation with modular handler architecture.
|
||||
Main server class delegates tool calls to specialized handlers.
|
||||
Each handler manages a specific domain (PRs, branches, reviews, files, search).
|
||||
|
||||
component_relationships: |
|
||||
- BitbucketMCPServer (main) → Handler classes → BitbucketApiClient → Axios
|
||||
- Tool definitions → MCP SDK → Client applications
|
||||
- Type guards validate inputs → Handlers process → Formatters standardize output
|
||||
|
||||
design_patterns:
|
||||
- name: "Handler Pattern"
|
||||
category: "architecture"
|
||||
description: "Separate handler classes for different tool categories"
|
||||
usage: "Organizing related tools and maintaining single responsibility"
|
||||
implementation:
|
||||
- "PullRequestHandlers for PR lifecycle"
|
||||
- "BranchHandlers for branch operations"
|
||||
- "ReviewHandlers for code review tools"
|
||||
- "FileHandlers for file/directory operations"
|
||||
- "SearchHandlers for code search"
|
||||
example_files:
|
||||
- "src/handlers/*.ts"
|
||||
related_patterns:
|
||||
- "Dependency Injection"
|
||||
|
||||
- name: "API Client Abstraction"
|
||||
category: "integration"
|
||||
description: "Unified client handling both Cloud and Server APIs"
|
||||
usage: "Abstracting API differences between Bitbucket variants"
|
||||
implementation:
|
||||
- "Single makeRequest method for all HTTP operations"
|
||||
- "Automatic auth header selection (Bearer vs Basic)"
|
||||
- "Consistent error handling across variants"
|
||||
example_files:
|
||||
- "src/utils/api-client.ts"
|
||||
|
||||
- name: "Type Guard Pattern"
|
||||
category: "validation"
|
||||
description: "Runtime type checking for tool arguments"
|
||||
usage: "Ensuring type safety for dynamic tool inputs"
|
||||
implementation:
|
||||
- "Guard functions return type predicates"
|
||||
- "Comprehensive validation of required/optional fields"
|
||||
- "Array and nested object validation"
|
||||
example_files:
|
||||
- "src/types/guards.ts"
|
||||
|
||||
- name: "Response Formatting"
|
||||
category: "data_transformation"
|
||||
description: "Consistent response formatting across API variants"
|
||||
usage: "Normalizing different API response structures"
|
||||
implementation:
|
||||
- "formatServerResponse/formatCloudResponse for PRs"
|
||||
- "Unified FormattedXXX interfaces"
|
||||
- "Separate formatters for different data types"
|
||||
example_files:
|
||||
- "src/utils/formatters.ts"
|
||||
|
||||
project_specific_patterns:
|
||||
mcp_patterns:
|
||||
- name: "Tool Definition Structure"
|
||||
description: "Standardized tool definition with inputSchema"
|
||||
implementation:
|
||||
- "Name, description, and JSON schema for each tool"
|
||||
- "Required vs optional parameter specification"
|
||||
- "Enum constraints for valid values"
|
||||
|
||||
- name: "Error Response Pattern"
|
||||
description: "Consistent error handling and reporting"
|
||||
implementation:
|
||||
- "Return isError: true for tool failures"
|
||||
- "Include detailed error messages"
|
||||
- "Provide context-specific error details"
|
||||
|
||||
bitbucket_patterns:
|
||||
- name: "Pagination Pattern"
|
||||
description: "Consistent pagination across list operations"
|
||||
implementation:
|
||||
- "limit and start parameters"
|
||||
- "has_more and next_start in responses"
|
||||
- "total_count for result sets"
|
||||
|
||||
- name: "Dual API Support"
|
||||
description: "Supporting both Cloud and Server APIs"
|
||||
implementation:
|
||||
- "isServer flag determines API paths"
|
||||
- "Different parameter names mapped appropriately"
|
||||
- "Response structure normalization"
|
||||
|
||||
code_patterns:
|
||||
- name: "Smart Truncation"
|
||||
description: "Intelligent file content truncation"
|
||||
implementation:
|
||||
- "File type-based default limits"
|
||||
- "Size-based automatic truncation"
|
||||
- "Line range selection support"
|
||||
|
||||
- name: "Code Snippet Matching"
|
||||
description: "Finding line numbers from code snippets"
|
||||
implementation:
|
||||
- "Exact text matching with context"
|
||||
- "Confidence scoring for multiple matches"
|
||||
- "Strategy selection (strict vs best)"
|
109
memory-bank/techContext.yml
Normal file
109
memory-bank/techContext.yml
Normal file
|
@ -0,0 +1,109 @@
|
|||
# Technical Context - Bitbucket MCP Server
|
||||
|
||||
core_technologies:
|
||||
language: "TypeScript"
|
||||
version: "5.8.3"
|
||||
module_system: "ES Modules"
|
||||
runtime:
|
||||
name: "Node.js"
|
||||
version: ">= 16.0.0"
|
||||
package_manager: "npm"
|
||||
|
||||
libraries_and_bindings:
|
||||
- name: "@modelcontextprotocol/sdk"
|
||||
version: "^1.12.1"
|
||||
purpose: "MCP protocol implementation"
|
||||
|
||||
- name: "axios"
|
||||
version: "^1.10.0"
|
||||
purpose: "HTTP client for API requests"
|
||||
|
||||
- name: "minimatch"
|
||||
version: "^9.0.3"
|
||||
purpose: "Glob pattern matching for file filtering"
|
||||
|
||||
development_environment:
|
||||
build_tools:
|
||||
- "TypeScript compiler (tsc)"
|
||||
- "npm scripts for build automation"
|
||||
|
||||
commands:
|
||||
build: "npm run build"
|
||||
dev: "npm run dev"
|
||||
start: "npm start"
|
||||
publish: "npm publish"
|
||||
|
||||
project_structure:
|
||||
src_directory: "src/"
|
||||
build_directory: "build/"
|
||||
entry_point: "src/index.ts"
|
||||
compiled_entry: "build/index.js"
|
||||
|
||||
technical_patterns:
|
||||
- name: "Shebang for CLI execution"
|
||||
description: "#!/usr/bin/env node at top of index.ts"
|
||||
usage: "Enables direct execution as CLI tool"
|
||||
|
||||
- name: "ES Module imports"
|
||||
description: "Using .js extensions in TypeScript imports"
|
||||
usage: "Required for ES module compatibility"
|
||||
examples:
|
||||
- "import { Server } from '@modelcontextprotocol/sdk/server/index.js'"
|
||||
|
||||
- name: "Type-safe error handling"
|
||||
description: "Custom ApiError interface with typed errors"
|
||||
usage: "Consistent error handling across API calls"
|
||||
|
||||
- name: "Environment variable configuration"
|
||||
description: "Process.env for authentication and base URL"
|
||||
usage: "Flexible configuration without code changes"
|
||||
|
||||
api_integration:
|
||||
bitbucket_cloud:
|
||||
base_url: "https://api.bitbucket.org/2.0"
|
||||
auth_method: "Basic Auth with App Password"
|
||||
api_style: "RESTful with JSON"
|
||||
pagination: "page-based with pagelen parameter"
|
||||
|
||||
bitbucket_server:
|
||||
base_url: "Custom URL (e.g., https://bitbucket.company.com)"
|
||||
auth_method: "Bearer token (HTTP Access Token)"
|
||||
api_style: "RESTful with JSON"
|
||||
pagination: "offset-based with start/limit"
|
||||
api_version: "/rest/api/1.0"
|
||||
search_api: "/rest/search/latest"
|
||||
|
||||
deployment:
|
||||
package_name: "@nexus2520/bitbucket-mcp-server"
|
||||
registry: "npm public registry"
|
||||
distribution:
|
||||
- "Compiled JavaScript in build/"
|
||||
- "Type definitions excluded"
|
||||
- "Source maps excluded"
|
||||
|
||||
execution_methods:
|
||||
- "npx -y @nexus2520/bitbucket-mcp-server"
|
||||
- "Direct node execution after install"
|
||||
- "MCP client integration"
|
||||
|
||||
security_considerations:
|
||||
- "Credentials stored in environment variables"
|
||||
- "No credential logging or exposure"
|
||||
- "HTTPS only for API communications"
|
||||
- "Token/password validation on startup"
|
||||
|
||||
performance_optimizations:
|
||||
- "Parallel API calls for PR details"
|
||||
- "Smart file truncation to prevent token overflow"
|
||||
- "Pagination for large result sets"
|
||||
- "Early exit on authentication failure"
|
||||
|
||||
compatibility:
|
||||
mcp_clients:
|
||||
- "Cline (VSCode extension)"
|
||||
- "Other MCP-compatible AI assistants"
|
||||
|
||||
bitbucket_versions:
|
||||
- "Bitbucket Cloud (latest API)"
|
||||
- "Bitbucket Server 7.x+"
|
||||
- "Bitbucket Data Center"
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@nexus2520/bitbucket-mcp-server",
|
||||
"version": "0.10.0",
|
||||
"version": "1.0.0",
|
||||
"description": "MCP server for Bitbucket API integration - supports both Cloud and Server",
|
||||
"type": "module",
|
||||
"main": "./build/index.js",
|
||||
|
|
100
src/handlers/search-handlers.ts
Normal file
100
src/handlers/search-handlers.ts
Normal file
|
@ -0,0 +1,100 @@
|
|||
import { BitbucketApiClient } from '../utils/api-client.js';
|
||||
import {
|
||||
BitbucketServerSearchRequest,
|
||||
BitbucketServerSearchResult,
|
||||
FormattedSearchResult
|
||||
} from '../types/bitbucket.js';
|
||||
import { formatSearchResults, formatCodeSearchOutput } from '../utils/formatters.js';
|
||||
|
||||
export class SearchHandlers {
|
||||
constructor(
|
||||
private apiClient: BitbucketApiClient,
|
||||
private baseUrl: string
|
||||
) {}
|
||||
|
||||
async handleSearchCode(args: any) {
|
||||
try {
|
||||
const { workspace, repository, search_query, file_pattern, limit = 25, start = 0 } = args;
|
||||
|
||||
if (!workspace || !search_query) {
|
||||
throw new Error('Workspace and search_query are required');
|
||||
}
|
||||
|
||||
// Build the query string
|
||||
let query = `project:${workspace}`;
|
||||
if (repository) {
|
||||
query += ` repo:${repository}`;
|
||||
}
|
||||
if (file_pattern) {
|
||||
query += ` path:${file_pattern}`;
|
||||
}
|
||||
query += ` ${search_query}`;
|
||||
|
||||
// Only works for Bitbucket Server currently
|
||||
if (!this.apiClient.getIsServer()) {
|
||||
throw new Error('Code search is currently only supported for Bitbucket Server');
|
||||
}
|
||||
|
||||
// Prepare the request payload
|
||||
const payload: BitbucketServerSearchRequest = {
|
||||
query: query.trim(),
|
||||
entities: {
|
||||
code: {
|
||||
start: start,
|
||||
limit: limit
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Make the API request (no query params needed, pagination is in payload)
|
||||
const response = await this.apiClient.makeRequest<BitbucketServerSearchResult>(
|
||||
'post',
|
||||
`/rest/search/latest/search?avatarSize=64`,
|
||||
payload
|
||||
);
|
||||
|
||||
const searchResult = response;
|
||||
|
||||
// Use simplified formatter for cleaner output
|
||||
const simplifiedOutput = formatCodeSearchOutput(searchResult);
|
||||
|
||||
// Prepare pagination info
|
||||
const hasMore = searchResult.code?.isLastPage === false;
|
||||
const nextStart = hasMore ? (searchResult.code?.nextStart || start + limit) : undefined;
|
||||
const totalCount = searchResult.code?.count || 0;
|
||||
|
||||
// Build a concise response
|
||||
let resultText = `Code search results for "${search_query}" in ${workspace}`;
|
||||
if (repository) {
|
||||
resultText += `/${repository}`;
|
||||
}
|
||||
resultText += `:\n\n${simplifiedOutput}`;
|
||||
|
||||
if (totalCount > 0) {
|
||||
resultText += `\n\nTotal matches: ${totalCount}`;
|
||||
if (hasMore) {
|
||||
resultText += ` (showing ${start + 1}-${start + (searchResult.code?.values?.length || 0)})`;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: resultText
|
||||
}]
|
||||
};
|
||||
} catch (error: any) {
|
||||
const errorMessage = error.response?.data?.errors?.[0]?.message || error.message;
|
||||
return {
|
||||
content: [{
|
||||
type: 'text',
|
||||
text: JSON.stringify({
|
||||
error: `Failed to search code: ${errorMessage}`,
|
||||
details: error.response?.data
|
||||
}, null, 2)
|
||||
}],
|
||||
isError: true
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import { PullRequestHandlers } from './handlers/pull-request-handlers.js';
|
|||
import { BranchHandlers } from './handlers/branch-handlers.js';
|
||||
import { ReviewHandlers } from './handlers/review-handlers.js';
|
||||
import { FileHandlers } from './handlers/file-handlers.js';
|
||||
import { SearchHandlers } from './handlers/search-handlers.js';
|
||||
import { toolDefinitions } from './tools/definitions.js';
|
||||
|
||||
// Get environment variables
|
||||
|
@ -35,12 +36,13 @@ class BitbucketMCPServer {
|
|||
private branchHandlers: BranchHandlers;
|
||||
private reviewHandlers: ReviewHandlers;
|
||||
private fileHandlers: FileHandlers;
|
||||
private searchHandlers: SearchHandlers;
|
||||
|
||||
constructor() {
|
||||
this.server = new Server(
|
||||
{
|
||||
name: 'bitbucket-mcp-server',
|
||||
version: '0.10.0',
|
||||
version: '1.0.0',
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
|
@ -66,6 +68,7 @@ class BitbucketMCPServer {
|
|||
this.branchHandlers = new BranchHandlers(this.apiClient, BITBUCKET_BASE_URL);
|
||||
this.reviewHandlers = new ReviewHandlers(this.apiClient, BITBUCKET_USERNAME!);
|
||||
this.fileHandlers = new FileHandlers(this.apiClient, BITBUCKET_BASE_URL);
|
||||
this.searchHandlers = new SearchHandlers(this.apiClient, BITBUCKET_BASE_URL);
|
||||
|
||||
this.setupToolHandlers();
|
||||
|
||||
|
@ -130,6 +133,10 @@ class BitbucketMCPServer {
|
|||
case 'get_file_content':
|
||||
return this.fileHandlers.handleGetFileContent(request.params.arguments);
|
||||
|
||||
// Search tools
|
||||
case 'search_code':
|
||||
return this.searchHandlers.handleSearchCode(request.params.arguments);
|
||||
|
||||
default:
|
||||
throw new McpError(
|
||||
ErrorCode.MethodNotFound,
|
||||
|
|
|
@ -607,4 +607,38 @@ export const toolDefinitions = [
|
|||
required: ['workspace', 'repository', 'pull_request_id'],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'search_code',
|
||||
description: 'Search for code across Bitbucket repositories (currently only supported for Bitbucket Server)',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
workspace: {
|
||||
type: 'string',
|
||||
description: 'Bitbucket workspace/project key (e.g., "PROJ")',
|
||||
},
|
||||
repository: {
|
||||
type: 'string',
|
||||
description: 'Repository slug to search in (optional, searches all repos if not specified)',
|
||||
},
|
||||
search_query: {
|
||||
type: 'string',
|
||||
description: 'The search term or phrase to look for in code',
|
||||
},
|
||||
file_pattern: {
|
||||
type: 'string',
|
||||
description: 'File path pattern to filter results (e.g., "*.java", "src/**/*.ts") (optional)',
|
||||
},
|
||||
limit: {
|
||||
type: 'number',
|
||||
description: 'Maximum number of results to return (default: 25)',
|
||||
},
|
||||
start: {
|
||||
type: 'number',
|
||||
description: 'Start index for pagination (default: 0)',
|
||||
},
|
||||
},
|
||||
required: ['workspace', 'search_query'],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
@ -443,3 +443,78 @@ export interface FormattedCommit {
|
|||
parents: string[];
|
||||
is_merge_commit: boolean;
|
||||
}
|
||||
|
||||
// Search types
|
||||
export interface BitbucketServerSearchRequest {
|
||||
query: string;
|
||||
entities: {
|
||||
code?: {
|
||||
start?: number;
|
||||
limit?: number;
|
||||
};
|
||||
commits?: {
|
||||
start?: number;
|
||||
limit?: number;
|
||||
};
|
||||
pull_requests?: {
|
||||
start?: number;
|
||||
limit?: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface BitbucketServerSearchResult {
|
||||
scope?: {
|
||||
repository?: {
|
||||
slug: string;
|
||||
name: string;
|
||||
project: {
|
||||
key: string;
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
type: string;
|
||||
};
|
||||
code?: {
|
||||
category: string;
|
||||
isLastPage: boolean;
|
||||
count: number;
|
||||
start: number;
|
||||
nextStart?: number;
|
||||
values: Array<{
|
||||
file: string; // Just the file path as string
|
||||
repository: {
|
||||
slug: string;
|
||||
name: string;
|
||||
project: {
|
||||
key: string;
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
hitContexts: Array<Array<{
|
||||
line: number;
|
||||
text: string; // HTML-formatted with <em> tags
|
||||
}>>;
|
||||
pathMatches: Array<any>;
|
||||
hitCount: number;
|
||||
}>;
|
||||
};
|
||||
query?: {
|
||||
substituted: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface FormattedSearchResult {
|
||||
file_path: string;
|
||||
file_name: string;
|
||||
repository: string;
|
||||
project: string;
|
||||
matches: Array<{
|
||||
line_number: number;
|
||||
line_content: string;
|
||||
highlighted_segments: Array<{
|
||||
text: string;
|
||||
is_match: boolean;
|
||||
}>;
|
||||
}>;
|
||||
}
|
||||
|
|
|
@ -305,3 +305,22 @@ export const isListPrCommitsArgs = (
|
|||
typeof args.pull_request_id === 'number' &&
|
||||
(args.limit === undefined || typeof args.limit === 'number') &&
|
||||
(args.start === undefined || typeof args.start === 'number');
|
||||
|
||||
export const isSearchCodeArgs = (
|
||||
args: any
|
||||
): args is {
|
||||
workspace: string;
|
||||
repository?: string;
|
||||
search_query: string;
|
||||
file_pattern?: string;
|
||||
limit?: number;
|
||||
start?: number;
|
||||
} =>
|
||||
typeof args === 'object' &&
|
||||
args !== null &&
|
||||
typeof args.workspace === 'string' &&
|
||||
typeof args.search_query === 'string' &&
|
||||
(args.repository === undefined || typeof args.repository === 'string') &&
|
||||
(args.file_pattern === undefined || typeof args.file_pattern === 'string') &&
|
||||
(args.limit === undefined || typeof args.limit === 'number') &&
|
||||
(args.start === undefined || typeof args.start === 'number');
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import {
|
||||
BitbucketServerPullRequest,
|
||||
BitbucketCloudPullRequest,
|
||||
import {
|
||||
BitbucketServerPullRequest,
|
||||
BitbucketCloudPullRequest,
|
||||
MergeInfo,
|
||||
BitbucketServerCommit,
|
||||
BitbucketCloudCommit,
|
||||
FormattedCommit
|
||||
FormattedCommit,
|
||||
BitbucketServerSearchResult,
|
||||
FormattedSearchResult
|
||||
} from '../types/bitbucket.js';
|
||||
|
||||
export function formatServerResponse(
|
||||
|
@ -116,3 +118,136 @@ export function formatCloudCommit(commit: BitbucketCloudCommit): FormattedCommit
|
|||
is_merge_commit: commit.parents.length > 1,
|
||||
};
|
||||
}
|
||||
|
||||
export function formatSearchResults(searchResult: BitbucketServerSearchResult): FormattedSearchResult[] {
|
||||
const results: FormattedSearchResult[] = [];
|
||||
|
||||
if (!searchResult.code?.values) {
|
||||
return results;
|
||||
}
|
||||
|
||||
for (const value of searchResult.code.values) {
|
||||
// Extract file name from path
|
||||
const fileName = value.file.split('/').pop() || value.file;
|
||||
|
||||
const formattedResult: FormattedSearchResult = {
|
||||
file_path: value.file,
|
||||
file_name: fileName,
|
||||
repository: value.repository.slug,
|
||||
project: value.repository.project.key,
|
||||
matches: []
|
||||
};
|
||||
|
||||
// Process hitContexts (array of arrays of line contexts)
|
||||
if (value.hitContexts && value.hitContexts.length > 0) {
|
||||
for (const contextGroup of value.hitContexts) {
|
||||
for (const lineContext of contextGroup) {
|
||||
// Parse HTML to extract text and highlight information
|
||||
const { text, segments } = parseHighlightedText(lineContext.text);
|
||||
|
||||
formattedResult.matches.push({
|
||||
line_number: lineContext.line,
|
||||
line_content: text,
|
||||
highlighted_segments: segments
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results.push(formattedResult);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// Helper function to parse HTML-formatted text with <em> tags
|
||||
function parseHighlightedText(htmlText: string): {
|
||||
text: string;
|
||||
segments: Array<{ text: string; is_match: boolean }>;
|
||||
} {
|
||||
// Decode HTML entities
|
||||
const decodedText = htmlText
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(///g, '/');
|
||||
|
||||
// Remove HTML tags and track highlighted segments
|
||||
const segments: Array<{ text: string; is_match: boolean }> = [];
|
||||
let plainText = '';
|
||||
let currentPos = 0;
|
||||
|
||||
// Match all <em> tags and their content
|
||||
const emRegex = /<em>(.*?)<\/em>/g;
|
||||
let lastEnd = 0;
|
||||
let match;
|
||||
|
||||
while ((match = emRegex.exec(decodedText)) !== null) {
|
||||
// Add non-highlighted text before this match
|
||||
if (match.index > lastEnd) {
|
||||
const beforeText = decodedText.substring(lastEnd, match.index);
|
||||
segments.push({ text: beforeText, is_match: false });
|
||||
plainText += beforeText;
|
||||
}
|
||||
|
||||
// Add highlighted text
|
||||
const highlightedText = match[1];
|
||||
segments.push({ text: highlightedText, is_match: true });
|
||||
plainText += highlightedText;
|
||||
|
||||
lastEnd = match.index + match[0].length;
|
||||
}
|
||||
|
||||
// Add any remaining non-highlighted text
|
||||
if (lastEnd < decodedText.length) {
|
||||
const remainingText = decodedText.substring(lastEnd);
|
||||
segments.push({ text: remainingText, is_match: false });
|
||||
plainText += remainingText;
|
||||
}
|
||||
|
||||
// If no <em> tags were found, the entire text is non-highlighted
|
||||
if (segments.length === 0) {
|
||||
segments.push({ text: decodedText, is_match: false });
|
||||
plainText = decodedText;
|
||||
}
|
||||
|
||||
return { text: plainText, segments };
|
||||
}
|
||||
|
||||
// Simplified formatter for MCP tool output
|
||||
export function formatCodeSearchOutput(searchResult: BitbucketServerSearchResult): string {
|
||||
if (!searchResult.code?.values || searchResult.code.values.length === 0) {
|
||||
return 'No results found';
|
||||
}
|
||||
|
||||
const outputLines: string[] = [];
|
||||
|
||||
for (const value of searchResult.code.values) {
|
||||
outputLines.push(`File: ${value.file}`);
|
||||
|
||||
// Process all hit contexts
|
||||
if (value.hitContexts && value.hitContexts.length > 0) {
|
||||
for (const contextGroup of value.hitContexts) {
|
||||
for (const lineContext of contextGroup) {
|
||||
// Remove HTML tags and decode entities
|
||||
const cleanText = lineContext.text
|
||||
.replace(/<em>/g, '')
|
||||
.replace(/<\/em>/g, '')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&')
|
||||
.replace(///g, '/')
|
||||
.replace(/'/g, "'");
|
||||
|
||||
outputLines.push(` Line ${lineContext.line}: ${cleanText}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outputLines.push(''); // Empty line between files
|
||||
}
|
||||
|
||||
return outputLines.join('\n').trim();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue