Skip to Content
πŸš€ vsync v1.0 is here! Sync your AI tools effortlessly. Star us on GitHub
Contributing

Contributing

Thank you for your interest in contributing to vsync! This guide will help you get started.

Ways to Contribute

⭐ Star the Repository

The simplest way to support the project is to star it on GitHubΒ . This helps others discover the tool.

πŸ› Report Bugs

Found a bug? Open an issueΒ  with:

  • Clear description of the problem
  • Steps to reproduce
  • Expected vs actual behavior
  • Environment details (OS, Node version, etc.)

πŸ’‘ Suggest Features

Have an idea? Open a feature requestΒ  describing:

  • The problem you’re trying to solve
  • Your proposed solution
  • Use cases and benefits
  • Any alternative approaches considered

πŸ”§ Submit Pull Requests

Code contributions are welcome! See the Development Setup section below.

πŸ“– Improve Documentation

Documentation improvements are valuable:

  • Fix typos or unclear explanations
  • Add examples or use cases
  • Translate to other languages
  • Improve code comments

Development Setup

Prerequisites

  • Node.js: >= 24.0.0 (for development)
  • pnpm: Package manager
  • Git: Version control

Clone the Repository

git clone https://github.com/nicepkg/vsync.git cd vsync

Install Dependencies

# Install pnpm if needed npm install -g pnpm # Install project dependencies cd cli pnpm install

Project Structure

vsync uses a pnpm monorepo:

vsync/ β”œβ”€β”€ pnpm-workspace.yaml # Workspace config β”œβ”€β”€ cli/ # CLI workspace package β”‚ β”œβ”€β”€ package.json # CLI dependencies β”‚ β”œβ”€β”€ tsconfig.json # TypeScript config β”‚ β”œβ”€β”€ src/ # Source code β”‚ β”‚ β”œβ”€β”€ cli/ # CLI entry point β”‚ β”‚ β”œβ”€β”€ commands/ # Command implementations β”‚ β”‚ β”œβ”€β”€ adapters/ # Tool adapters β”‚ β”‚ β”œβ”€β”€ core/ # Core logic β”‚ β”‚ β”œβ”€β”€ types/ # TypeScript types β”‚ β”‚ └── utils/ # Utilities β”‚ └── test/ # Tests (mirrors src/) └── website/ # Documentation website

Build the CLI

cd cli pnpm build

Run Tests

# Run all tests pnpm test # Run tests in watch mode pnpm test:watch # Run tests with coverage pnpm test:coverage # Type checking pnpm typecheck # Linting pnpm lint

Test the CLI Locally

# Link for local testing cd cli npm link # Now you can run vsync globally vsync --version

Development Workflow

vsync follows a Test-Driven Development (TDD) approach:

1. Read Current Task

Check TASKS.md for the current phase and task:

cat TASKS.md

2. Write Tests First

Before implementing, write tests:

// cli/test/commands/my-feature.test.ts import { describe, it, expect } from 'vitest'; describe('My Feature', () => { it('should do something', () => { // Test implementation }); });

3. Implement the Feature

Write the code to make tests pass:

// cli/src/commands/my-feature.ts export function myFeature() { // Implementation }

4. Validate

# Run tests pnpm test # Type check pnpm typecheck # Lint pnpm lint

5. Mark Task Complete

Update TASKS.md:

- [x] Implement my feature

6. Commit Changes

Follow Angular commit convention:

git add . git commit -m "feat: add my awesome feature Implements the feature described in task X. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"

Code Style and Standards

TypeScript

  • Strict mode: All code must pass strict: true
  • No any: Use proper types
  • Explicit types: For public APIs
// βœ… Good export function calculateHash(content: string): string { return crypto.createHash('sha256').update(content).digest('hex'); } // ❌ Bad export function calculateHash(content: any): any { return crypto.createHash('sha256').update(content).digest('hex'); }

Testing

  • Test coverage: Aim for >80%
  • Unit tests: For individual functions
  • Integration tests: For command workflows
  • Mock file system: Use mock-fs for file operations
import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import mock from 'mock-fs'; describe('My Feature', () => { beforeEach(() => { mock({ '/fake/path': { 'file.txt': 'content' } }); }); afterEach(() => { mock.restore(); }); it('should work', async () => { // Test with mocked filesystem }); });

Commit Convention

Follow Angular commit conventionΒ :

<type>(<scope>): <subject> <body> <footer>

Types:

  • feat - New feature
  • fix - Bug fix
  • docs - Documentation changes
  • refactor - Code refactoring
  • test - Test changes
  • chore - Build/tooling changes

Examples:

git commit -m "feat(adapters): add codex TOML support" git commit -m "fix(sync): preserve JSONC comments in OpenCode" git commit -m "docs(readme): update installation instructions"

Architecture Guidelines

Adding a New Tool Adapter

  1. Create adapter file: cli/src/adapters/my-tool-adapter.ts
import { BaseAdapter } from './base-adapter'; import type { ToolAdapter } from '@src/types/adapter'; export class MyToolAdapter extends BaseAdapter implements ToolAdapter { readonly name = 'my-tool'; async readSkills(): Promise<Skill[]> { // Implementation } async writeSkills(skills: Skill[]): Promise<WriteResult> { // Implementation } // ... other methods }
  1. Register adapter: cli/src/adapters/registry.ts
export function getAdapter(options: AdapterOptions): ToolAdapter { switch (options.tool) { case 'my-tool': return new MyToolAdapter(options); // ... other cases } }
  1. Write tests: cli/test/adapters/my-tool-adapter.test.ts

  2. Update types: cli/src/types/config.ts

export type ToolName = | 'claude-code' | 'cursor' | 'opencode' | 'codex' | 'my-tool'; // Add here

Adding a New Command

  1. Create command file: cli/src/commands/my-command.ts
import { Command } from 'commander'; export function createMyCommand(): Command { const command = new Command('my-command'); command .description('Description of my command') .option('--option', 'Option description') .action(async (options) => { // Command implementation }); return command; }
  1. Register command: cli/src/cli/index.ts
import { createMyCommand } from '@src/commands/my-command'; program.addCommand(createMyCommand());
  1. Write tests: cli/test/commands/my-command.test.ts

  2. Add i18n strings: cli/src/utils/i18n.ts


Critical Rules

1. Never Expand Environment Variables

// ❌ WRONG - Expands variables const config = { env: { TOKEN: process.env.GITHUB_TOKEN // Actual value! } }; // βœ… CORRECT - Preserves syntax const config = { env: { TOKEN: "${env:GITHUB_TOKEN}" // Reference preserved } };

2. Always Use Atomic Writes

import { atomicWrite } from '@src/utils/atomic-write'; // ❌ WRONG - Not crash-safe await fs.writeFile(path, content); // βœ… CORRECT - Atomic operation await atomicWrite(path, content);

3. Follow TDD Workflow

# 1. Write test first vim test/my-feature.test.ts # 2. Run test (should fail) pnpm test # 3. Implement feature vim src/my-feature.ts # 4. Run test (should pass) pnpm test # 5. Commit git commit -m "feat: add my feature"

4. Respect Config Format Precision

Each tool has different MCP formats:

// OpenCode - unique format! { "mcp": { // Not "mcpServers"! "server": { "type": "local", // Required! "environment": { // Not "env"! "VAR": "{env:VAR}" // Curly braces! } } } }

See docs/prd.md Section 2.2 for complete format specifications.


Pull Request Process

  1. Fork the repository

  2. Create a feature branch:

    git checkout -b feat/my-feature
  3. Make your changes following the guidelines above

  4. Run tests and checks:

    pnpm test pnpm typecheck pnpm lint
  5. Commit with conventional commits:

    git commit -m "feat: add my feature"
  6. Push to your fork:

    git push origin feat/my-feature
  7. Create Pull Request on GitHub with:

    • Clear title and description
    • Link to related issues
    • Screenshots (if UI changes)
    • Test results

PR Checklist

  • Tests pass (pnpm test)
  • Type checking passes (pnpm typecheck)
  • Linting passes (pnpm lint)
  • Follows TDD workflow
  • Config formats match PRD exactly
  • Environment variables not expanded
  • Atomic writes used
  • Conventional commit messages
  • Documentation updated (if needed)

Testing Guidelines

What to Test

  1. Happy paths: Normal usage scenarios
  2. Edge cases: Empty configs, missing files, etc.
  3. Error handling: Invalid inputs, permission errors
  4. Format conversion: Correct syntax transformation
  5. Atomic operations: Rollback on errors

Test Structure

describe('Feature Name', () => { describe('when condition A', () => { it('should do X', () => { // Arrange const input = setupInput(); // Act const result = performAction(input); // Assert expect(result).toBe(expected); }); }); describe('when condition B', () => { it('should do Y', () => { // ... }); }); });

Code Review

All PRs are reviewed for:

  • Correctness: Does it work as intended?
  • Tests: Are there adequate tests?
  • Types: Are types correct and precise?
  • Style: Does it follow project conventions?
  • Documentation: Are docs updated if needed?
  • Performance: Are there any inefficiencies?

Documentation Standards

README Updates

Keep README.md in sync with:

  • Feature additions
  • Command changes
  • Configuration updates

Code Documentation

/** * Brief description of function * * Longer explanation if needed. * * @param paramName - Parameter description * @returns Return value description * @throws {ErrorType} When this error occurs * * @example * ```typescript * const result = myFunction('input'); * console.log(result); // 'output' * ``` */ export function myFunction(paramName: string): string { // Implementation }

Getting Help

Need help contributing?


Code of Conduct

Be respectful and constructive:

  • Welcome newcomers
  • Be patient with questions
  • Provide constructive feedback
  • Focus on the code, not the person

License

By contributing, you agree that your contributions will be licensed under the MIT License.


Recognition

Contributors are recognized in:

  • README.md contributors section
  • Release notes
  • GitHub contributors graph

Thank you for making vsync better! πŸŽ‰

Last updated on