Welcome to contribute to the RunVSAgent project!
Thank you for your interest in contributing to RunVSAgent! RunVSAgent is an innovative cross-platform development tool that enables developers to run VSCode-based coding agents and extensions within JetBrains IDEs. Your contributions help us build a better development experience for the community.
RunVSAgent bridges the gap between VSCode extensions and JetBrains IDEs through:
- Cross-platform compatibility: Run VSCode agents in IntelliJ IDEA, WebStorm, PyCharm, and other JetBrains IDEs
- Seamless integration: Native IDE experience with VSCode extension capabilities
- High-performance communication: RPC-based architecture for real-time interaction
- Extensible design: Plugin architecture supporting various coding agents
We welcome various types of contributions:
- 🐛 Bug Reports: Help us identify and fix issues
- ✨ Feature Requests: Suggest new features and improvements
- 💻 Code Contributions: Implement features, fix bugs, improve performance
- 📚 Documentation: Improve guides, API docs, and examples
- 🧪 Testing: Write tests, improve test coverage, test new features
- 🎨 UI/UX: Enhance user interface and experience
- 🌐 Localization: Translate the project to different languages
- 📝 Code Reviews: Review pull requests and provide feedback
Before you begin, ensure you have the following installed:
- Node.js: Version 18.0.0 or later
- npm: Version 8.0.0 or later (comes with Node.js)
- Git: Any recent version
- JDK: Version 17 or later (for JetBrains plugin development)
- JetBrains IDE: IntelliJ IDEA 2023.1+ (for testing)
- pnpm: Alternative package manager (recommended for faster installs)
- shellcheck: For shell script linting
- Gradle: Will be downloaded automatically by the wrapper
- Fork and Clone the Repository
# Fork the repository on GitHub, then clone your fork
git clone https://github.com/YOUR_USERNAME/RunVSAgent.git
cd RunVSAgent
# Add the original repository as upstream
git remote add upstream https://github.com/original-org/RunVSAgent.git- Initialize Development Environment
# Run the setup script to initialize everything
./scripts/setup.sh
# For verbose output (recommended for first-time setup)
./scripts/setup.sh --verbose- Verify Installation
# Build the project to ensure everything is working
./scripts/build.sh
# Run tests
./scripts/test.shFor active development, you can run components in development mode:
# Start extension host in development mode
cd extension_host
npm run dev
# In another terminal, run JetBrains plugin in development mode
cd jetbrains_plugin
./gradlew runIde- Open the project root directory in IntelliJ IDEA
- Import the Gradle project from
jetbrains_plugin/ - Configure JDK 17+ in Project Settings
- Install recommended plugins:
- Kotlin
- Gradle
- Git
For TypeScript development:
- Open the
extension_host/directory in VSCode - Install recommended extensions (see
.vscode/extensions.json) - Use the provided debug configurations
We use a Git Flow-inspired branching strategy:
main: Production-ready codedevelop: Integration branch for featuresfeature/*: Feature development branchesbugfix/*: Bug fix brancheshotfix/*: Critical fixes for productionrelease/*: Release preparation branches
feature/add-new-agent-support
bugfix/fix-rpc-connection-issue
hotfix/critical-security-patch
release/v1.2.0
docs/update-contributing-guide
test/improve-unit-test-coverage
- Create a Feature Branch
# Sync with upstream
git fetch upstream
git checkout main
git merge upstream/main
# Create and switch to a new branch
git checkout -b feature/your-feature-name- Make Your Changes
- Write clean, well-documented code
- Follow the coding standards (see below)
- Add tests for new functionality
- Update documentation as needed
- Test Your Changes
# Run all tests
./scripts/test.sh
# Run specific test types
./scripts/test.sh unit
./scripts/test.sh lint
./scripts/test.sh integration
# Build the project
./scripts/build.sh --mode debug- Commit Your Changes
Follow the conventional commit format:
git add .
git commit -m "feat: add support for new VSCode agent API"- Push and Create Pull Request
git push origin feature/your-feature-nameThen create a pull request on GitHub with:
- Clear title and description
- Reference to related issues
- Screenshots/demos if applicable
- Checklist completion
We follow the Conventional Commits specification:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasksperf: Performance improvementsci: CI/CD changes
Examples:
feat(extension-host): add support for WebView API
fix(jetbrains-plugin): resolve RPC connection timeout issue
docs: update installation guide for Windows users
test(rpc): add unit tests for message serialization
- Use TypeScript 5.0+ features
- Follow ESLint and Prettier configurations
- Use camelCase for variables and functions
- Use PascalCase for classes and interfaces
- Use UPPER_SNAKE_CASE for constants
// ✅ Good
interface ExtensionConfig {
readonly name: string;
readonly version: string;
readonly enabled: boolean;
}
class ExtensionManager {
private readonly extensions = new Map<string, Extension>();
public async loadExtension(config: ExtensionConfig): Promise<void> {
// Implementation
}
}
// ❌ Bad
interface extensionconfig {
name: string;
version: string;
enabled: boolean;
}
class extension_manager {
extensions = {};
loadExtension(config) {
// Implementation
}
}// ✅ Good - Proper error handling
async function loadExtension(path: string): Promise<Extension> {
try {
const config = await readExtensionConfig(path);
return new Extension(config);
} catch (error) {
logger.error(`Failed to load extension from ${path}:`, error);
throw new ExtensionLoadError(`Cannot load extension: ${error.message}`);
}
}
// ❌ Bad - Silent failures
async function loadExtension(path: string): Promise<Extension | null> {
try {
const config = await readExtensionConfig(path);
return new Extension(config);
} catch (error) {
return null; // Silent failure
}
}- Follow Kotlin Coding Conventions
- Use ktlint for formatting
- Use camelCase for functions and properties
- Use PascalCase for classes
- Use UPPER_SNAKE_CASE for constants
// ✅ Good
class ExtensionHostManager(
private val project: Project,
private val logger: Logger
) {
companion object {
private const val DEFAULT_TIMEOUT = 5000L
private const val MAX_RETRY_COUNT = 3
}
suspend fun startExtensionHost(): Result<ExtensionHost> {
return try {
val host = createExtensionHost()
host.start()
Result.success(host)
} catch (e: Exception) {
logger.error("Failed to start extension host", e)
Result.failure(e)
}
}
}
// ❌ Bad
class extensionhostmanager(project: Project, logger: Logger) {
fun startExtensionHost(): ExtensionHost? {
try {
val host = createExtensionHost()
host.start()
return host
} catch (e: Exception) {
return null
}
}
}// ✅ Good - Proper coroutine usage
class RpcManager(private val scope: CoroutineScope) {
suspend fun sendMessage(message: RpcMessage): RpcResponse {
return withContext(Dispatchers.IO) {
withTimeout(5000) {
rpcChannel.send(message)
}
}
}
}
// ❌ Bad - Blocking operations
class RpcManager {
fun sendMessage(message: RpcMessage): RpcResponse {
return runBlocking {
rpcChannel.send(message) // Blocks the thread
}
}
}# Format TypeScript code
cd extension_host
npm run format
# Format Kotlin code
cd jetbrains_plugin
./gradlew ktlintFormat
# Check formatting
./scripts/test.sh lintUse the provided .editorconfig file:
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{ts,js}]
indent_style = space
indent_size = 2
[*.kt]
indent_style = space
indent_size = 4
[*.md]
trim_trailing_whitespace = false/**
* Manages the lifecycle of VSCode extensions within the JetBrains environment.
*
* This class handles:
* - Extension loading and unloading
* - Communication with the extension host
* - Error handling and recovery
*
* @example
* ```typescript
* const manager = new ExtensionManager(config);
* await manager.loadExtension('path/to/extension');
* ```
*/
class ExtensionManager {
/**
* Loads an extension from the specified path.
*
* @param extensionPath - Absolute path to the extension directory
* @returns Promise that resolves to the loaded extension
* @throws ExtensionLoadError if the extension cannot be loaded
*/
async loadExtension(extensionPath: string): Promise<Extension> {
// Implementation
}
}- Use clear, concise language
- Include code examples
- Provide step-by-step instructions
- Add diagrams for complex concepts
- Keep documentation up-to-date with code changes
graph TB
subgraph "JetBrains IDE"
A[JetBrains Plugin<br/>Kotlin]
B[UI Integration]
C[Editor Bridge]
end
subgraph "Extension Host"
D[Node.js Runtime]
E[VSCode API Layer]
F[Agent Manager]
end
subgraph "VSCode Agents"
G[Coding Agent 1]
H[Coding Agent 2]
I[Other Extensions]
end
A <-->|RPC Communication| D
B --> A
C --> A
E --> D
F --> D
G --> E
H --> E
I --> E
Purpose: Integrates with JetBrains IDEs and provides the bridge to VSCode extensions.
Key Modules:
- Core (
core/): Main plugin logic, extension host management - Actions (
actions/): IDE actions and commands - Actors (
actors/): RPC communication handlers - Editor (
editor/): Editor integration and document management - UI (
ui/): User interface components - WebView (
webview/): WebView support for extensions
Architecture Patterns:
- Actor Model: For RPC communication
- Observer Pattern: For event handling
- Factory Pattern: For component creation
Purpose: Provides VSCode API compatibility layer and manages extension lifecycle.
Key Modules:
- Main (
main.ts): Entry point and initialization - Extension Manager (
extensionManager.ts): Extension lifecycle management - RPC Manager (
rpcManager.ts): Communication with JetBrains plugin - WebView Manager (
webViewManager.ts): WebView support
Architecture Patterns:
- Module Pattern: For code organization
- Event-Driven Architecture: For extension communication
- Proxy Pattern: For API compatibility
interface RpcMessage {
id: string;
method: string;
params: unknown[];
timestamp: number;
}
interface RpcResponse {
id: string;
result?: unknown;
error?: RpcError;
timestamp: number;
}- JetBrains Plugin → Extension Host: Command execution
- Extension Host → VSCode Extension: API calls
- VSCode Extension → Extension Host: Responses/Events
- Extension Host → JetBrains Plugin: Results/Notifications
- Define the API interface in
extension_host/src/api/ - Implement the API handler in the extension host
- Add RPC communication between plugin and host
- Create corresponding actor in the JetBrains plugin
- Add tests for the new functionality
- Extend the base agent interface
- Implement agent-specific logic
- Add configuration options
- Update documentation
-
Design Phase:
- Create design document
- Review with maintainers
- Consider backward compatibility
-
Implementation Phase:
- Follow TDD approach
- Implement in small, reviewable chunks
- Add comprehensive tests
-
Integration Phase:
- Test with real extensions
- Performance testing
- Documentation updates
- Minimize RPC calls: Batch operations when possible
- Use async/await: Avoid blocking operations
- Memory management: Dispose resources properly
- Lazy loading: Load extensions on demand
We use a multi-layered testing approach:
- Unit Tests: Test individual components in isolation
- Integration Tests: Test component interactions
- End-to-End Tests: Test complete user workflows
- Performance Tests: Ensure acceptable performance
- Compatibility Tests: Test with different IDE versions
# Run all tests
./scripts/test.sh
# Run specific test types
./scripts/test.sh unit
./scripts/test.sh integration
./scripts/test.sh lint
# Run tests with coverage
./scripts/test.sh --coverage
# Run tests in watch mode (for development)
./scripts/test.sh --watch// extension_host/src/__tests__/extensionManager.test.ts
import { ExtensionManager } from '../extensionManager';
import { mockExtensionConfig } from './mocks';
describe('ExtensionManager', () => {
let extensionManager: ExtensionManager;
beforeEach(() => {
extensionManager = new ExtensionManager();
});
afterEach(() => {
extensionManager.dispose();
});
describe('loadExtension', () => {
it('should load a valid extension', async () => {
// Arrange
const config = mockExtensionConfig();
// Act
const extension = await extensionManager.loadExtension(config);
// Assert
expect(extension).toBeDefined();
expect(extension.isActive).toBe(true);
});
it('should throw error for invalid extension', async () => {
// Arrange
const invalidConfig = { ...mockExtensionConfig(), name: '' };
// Act & Assert
await expect(extensionManager.loadExtension(invalidConfig))
.rejects.toThrow('Extension name cannot be empty');
});
});
});// jetbrains_plugin/src/test/kotlin/ExtensionHostManagerTest.kt
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.*
import kotlinx.coroutines.test.runTest
class ExtensionHostManagerTest {
private lateinit var manager: ExtensionHostManager
private lateinit var mockProject: Project
@BeforeEach
fun setUp() {
mockProject = createMockProject()
manager = ExtensionHostManager(mockProject)
}
@AfterEach
fun tearDown() {
manager.dispose()
}
@Test
fun `should start extension host successfully`() = runTest {
// Arrange
val config = createValidConfig()
// Act
val result = manager.startExtensionHost(config)
// Assert
assertTrue(result.isSuccess)
assertTrue(manager.isRunning)
}
@Test
fun `should handle startup failure gracefully`() = runTest {
// Arrange
val invalidConfig = createInvalidConfig()
// Act
val result = manager.startExtensionHost(invalidConfig)
// Assert
assertTrue(result.isFailure)
assertFalse(manager.isRunning)
}
}// integration-tests/rpc-communication.test.ts
describe('RPC Communication Integration', () => {
let jetbrainsPlugin: MockJetBrainsPlugin;
let extensionHost: ExtensionHost;
beforeAll(async () => {
jetbrainsPlugin = new MockJetBrainsPlugin();
extensionHost = new ExtensionHost();
await jetbrainsPlugin.start();
await extensionHost.start();
await establishConnection(jetbrainsPlugin, extensionHost);
});
afterAll(async () => {
await extensionHost.stop();
await jetbrainsPlugin.stop();
});
it('should execute commands end-to-end', async () => {
// Arrange
const command = 'extension.test.command';
const params = { text: 'Hello, World!' };
// Act
const response = await jetbrainsPlugin.executeCommand(command, params);
// Assert
expect(response.success).toBe(true);
expect(response.result).toContain('Hello, World!');
});
});module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.test.ts'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/__tests__/**',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
setupFilesAfterEnv: ['<rootDir>/src/__tests__/setup.ts'],
};Create reusable test utilities:
// extension_host/src/__tests__/mocks/index.ts
export function mockExtensionConfig(): ExtensionConfig {
return {
name: 'test-extension',
version: '1.0.0',
main: './main.js',
contributes: {
commands: [
{
command: 'test.command',
title: 'Test Command'
}
]
}
};
}
export function createMockRpcChannel(): MockRpcChannel {
return new MockRpcChannel();
}When reporting bugs, please include:
-
Clear Title: Descriptive summary of the issue
-
Environment Details:
- Operating System (Windows/macOS/Linux)
- JetBrains IDE version
- Node.js version
- RunVSAgent version
-
Steps to Reproduce:
1. Open IntelliJ IDEA 2. Install RunVSAgent plugin 3. Load a VSCode extension 4. Execute command X 5. Observe the error -
Expected vs Actual Behavior
-
Screenshots/Logs: If applicable
-
Additional Context: Any relevant information
## Bug Description
A clear and concise description of the bug.
## Environment
- OS: [e.g., macOS 13.0]
- IDE: [e.g., IntelliJ IDEA 2023.2]
- Node.js: [e.g., 18.17.0]
- RunVSAgent: [e.g., 1.2.0]
## Steps to Reproduce
1. Step one
2. Step two
3. Step three
## Expected Behavior
What you expected to happen.
## Actual Behavior
What actually happened.
## Screenshots/Logs
If applicable, add screenshots or log files.
## Additional Context
Any other context about the problem.For feature requests, please provide:
- Problem Statement: What problem does this solve?
- Proposed Solution: How should it work?
- Alternatives Considered: Other approaches you've thought about
- Use Cases: Real-world scenarios where this would be useful
- Implementation Ideas: Technical suggestions (optional)
## Feature Description
A clear and concise description of the feature.
## Problem Statement
What problem does this feature solve?
## Proposed Solution
How should this feature work?
## Use Cases
- Use case 1: Description
- Use case 2: Description
## Alternatives Considered
Other solutions you've considered.
## Additional Context
Any other context or screenshots about the feature request.We use the following labels to categorize issues:
-
Type Labels:
bug: Something isn't workingenhancement: New feature or requestdocumentation: Improvements or additions to documentationquestion: Further information is requested
-
Priority Labels:
priority/critical: Critical issues that block usagepriority/high: Important issuespriority/medium: Standard prioritypriority/low: Nice to have
-
Component Labels:
component/extension-host: Extension host relatedcomponent/jetbrains-plugin: JetBrains plugin relatedcomponent/rpc: RPC communication relatedcomponent/build: Build system related
-
Status Labels:
status/needs-triage: Needs initial reviewstatus/in-progress: Currently being worked onstatus/blocked: Blocked by external factorsstatus/ready-for-review: Ready for code review
- GitHub Issues: Bug reports and feature requests
- GitHub Discussions: General questions and community discussions
- Pull Request Reviews: Code review and technical discussions
We are committed to providing a welcoming and inclusive environment for all contributors. Please read and follow our Code of Conduct.
Positive behavior includes:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Unacceptable behavior includes:
- The use of sexualized language or imagery
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information without explicit permission
- Other conduct which could reasonably be considered inappropriate
If you need help:
- Check the documentation: README, BUILD.md, and this guide
- Search existing issues: Your question might already be answered
- Ask in GitHub Discussions: For general questions
- Create an issue: For specific problems or bugs
We believe in recognizing contributors:
- Contributors List: All contributors are listed in the project
- Release Notes: Significant contributions are mentioned in release notes
- Special Recognition: Outstanding contributors may receive special recognition
We follow Semantic Versioning (SemVer):
- MAJOR version: Incompatible API changes
- MINOR version: Backward-compatible functionality additions
- PATCH version: Backward-compatible bug fixes
Example: 1.2.3
1: Major version2: Minor version3: Patch version
# Create release branch
git checkout -b release/v1.2.0
# Update version numbers
# - extension_host/package.json
# - jetbrains_plugin/build.gradle.kts
# - README.md
# Update CHANGELOG.md
# Add release notes and breaking changes
# Run full test suite
./scripts/test.sh
./scripts/build.sh- All tests pass
- Documentation is updated
- CHANGELOG.md is updated
- Version numbers are bumped
- Breaking changes are documented
- Migration guide is provided (if needed)
- Release notes are prepared
# Merge release branch to main
git checkout main
git merge release/v1.2.0
# Create and push tag
git tag -a v1.2.0 -m "Release version 1.2.0"
git push origin v1.2.0
# Create GitHub release with release notes# Merge back to develop
git checkout develop
git merge main
# Clean up release branch
git branch -d release/v1.2.0We maintain a detailed changelog following Keep a Changelog format:
# Changelog
## [Unreleased]
### Added
- New feature descriptions
### Changed
- Changes in existing functionality
### Deprecated
- Soon-to-be removed features
### Removed
- Now removed features
### Fixed
- Bug fixes
### Security
- Security improvements
## [1.2.0] - 2023-12-01
### Added
- Support for VSCode WebView API
- New RPC communication protocol
### Fixed
- Memory leak in extension host
- Connection timeout issuesWe strive to maintain backward compatibility:
- API Changes: Deprecated before removal
- Configuration Changes: Migration scripts provided
- Breaking Changes: Clearly documented with upgrade guides
- Support Policy: Support last 2 major versions
Ready to contribute? Here's your getting started checklist:
- ⭐ Star the repository
- 🍴 Fork the repository
- 📥 Clone your fork locally
- 🛠️ Run
./scripts/setup.shto set up the development environment - 🏗️ Build the project with
./scripts/build.sh - 🧪 Run tests with
./scripts/test.sh - 📖 Read through this contributing guide
- 🔍 Look for "good first issue" labels
- 💬 Join the community discussions
- 🌿 Create a feature branch
- ✅ Write tests for your changes
- 🧹 Run linting and formatting
- 📝 Update documentation if needed
- ✍️ Write a clear commit message
- 🔄 Create a pull request with a good description
- 🔄 Keep your fork synced with upstream
- 👀 Review other contributors' PRs
- 🐛 Help triage issues
- 📚 Improve documentation
- 🎯 Suggest new features or improvements
- WeCode-AI Team, Weibo Inc.
- GitHub: Project Repository
- Issues: GitHub Issues
- Documentation: Check README.md and BUILD.md first
- GitHub Issues: For bug reports and feature requests
- GitHub Discussions: For questions and community support
This project is licensed under the Apache License 2.0. See LICENSE for details.
By contributing to RunVSAgent, you agree that your contributions will be licensed under the Apache License 2.0.
Thank you for contributing to RunVSAgent! 🚀
Together, we're building the future of cross-platform development tools.