Thank you for your interest in contributing to CodeGuard! This document provides comprehensive guidelines and instructions for contributing to the project.
- Code of Conduct
- Getting Started
- Development Setup
- Project Structure
- Development Workflow
- Testing
- Code Style
- Pull Request Process
- Adding New Features
- Performance Guidelines
- Documentation
We are committed to providing a welcoming and inclusive environment for all contributors. We expect:
- Respectful communication: Be kind and considerate in all interactions
- Constructive feedback: Focus on ideas, not individuals
- Inclusive language: Avoid discriminatory or offensive language
- Collaborative spirit: Help others learn and grow
If you experience or witness unacceptable behavior, please report it via GitHub Issues.
Before you begin, ensure you have:
- Node.js: Version 20.x or higher
- npm: Version 10.x or higher
- VSCode: Version 1.85.0 or higher
- Git: Latest version
- TypeScript: Familiarity with TypeScript 5.x
- Fork the repository on GitHub
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/codeguard.git cd codeguard - Add upstream remote:
git remote add upstream https://github.com/lekhanpro/CodeGuard.git
- Install dependencies:
npm install
- Compile the extension:
npm run compile
- Run tests:
npm test - Launch Extension Development Host:
- Press
F5in VSCode - Or run:
Debug: Start Debuggingfrom Command Palette
- Press
Install Node.js and npm:
# Using nvm (recommended)
nvm install 20
nvm use 20
# Verify installation
node --version # Should be 20.x
npm --version # Should be 10.xInstall VSCode:
- Download from https://code.visualstudio.com/
- Install recommended extensions (see
.vscode/extensions.json)
# Clone and setup
git clone https://github.com/YOUR_USERNAME/codeguard.git
cd codeguard
npm install
# Build the project
npm run compile
# Verify setup
npm testThe project includes VSCode configuration files:
.vscode/launch.json: Debug configurations.vscode/tasks.json: Build tasks.vscode/settings.json: Project settings.vscode/extensions.json: Recommended extensions
Recommended Extensions:
- ESLint
- Prettier
- TypeScript and JavaScript Language Features
- Vitest
Watch Mode (auto-compile on changes):
npm run watchDebug Mode:
- Open VSCode
- Press
F5or selectRun > Start Debugging - A new VSCode window opens with the extension loaded
- Set breakpoints in your code
- Trigger extension functionality to hit breakpoints
Testing in Watch Mode:
npm test -- --watchIssue: npm install fails
# Clear npm cache
npm cache clean --force
rm -rf node_modules package-lock.json
npm installIssue: TypeScript compilation errors
# Ensure TypeScript is installed
npm install -g typescript
tsc --version
# Clean and rebuild
npm run clean
npm run compileIssue: Extension doesn't load in debug mode
- Check
dist/extension.jsexists - Verify
package.jsonhas correctmainfield - Check Output panel for errors (View → Output → Extension Host)
codeguard/
├── .vscode/ # VSCode configuration
│ ├── launch.json # Debug configurations
│ ├── tasks.json # Build tasks
│ └── settings.json # Project settings
├── src/ # Source code
│ ├── analyzers/ # Code analysis modules
│ │ ├── Analyzer.ts # Base analyzer interface
│ │ ├── SecurityAnalyzer.ts
│ │ ├── SecretScanner.ts
│ │ ├── PerformanceAnalyzer.ts
│ │ └── CodeSmellDetector.ts
│ ├── ai/ # AI service integration
│ │ ├── AIService.ts # AI service coordinator
│ │ ├── OpenAIClient.ts
│ │ ├── ClaudeClient.ts
│ │ └── OllamaClient.ts
│ ├── cache/ # Caching implementation
│ │ └── CacheManager.ts # Two-level cache (memory + disk)
│ ├── config/ # Configuration management
│ │ └── ConfigurationManager.ts
│ ├── diagnostics/ # Diagnostic management
│ │ └── DiagnosticManager.ts
│ ├── engine/ # Analysis engine
│ │ ├── AnalysisEngine.ts # Main coordinator
│ │ ├── IncrementalAnalyzer.ts # Incremental analysis
│ │ ├── WorkerThreadManager.ts # Worker thread pool
│ │ └── AnalysisWorker.ts # Worker implementation
│ ├── providers/ # VSCode providers
│ │ ├── CodeActionProvider.ts # Quick fixes
│ │ ├── HoverProvider.ts # Hover information
│ │ └── StatusBarProvider.ts # Status bar integration
│ ├── privacy/ # Privacy monitoring
│ │ └── PrivacyMonitor.ts
│ ├── types/ # TypeScript type definitions
│ │ └── index.ts
│ ├── utils/ # Utility functions
│ │ └── debounce.ts
│ └── extension.ts # Extension entry point
├── dist/ # Compiled output (generated)
├── docs/ # Documentation
├── .kiro/ # Kiro spec files
│ └── specs/codeguard/
│ ├── requirements.md
│ ├── design.md
│ └── tasks.md
├── package.json # Extension manifest
├── tsconfig.json # TypeScript configuration
├── vitest.config.ts # Test configuration
├── .eslintrc.json # ESLint configuration
├── .gitignore # Git ignore rules
├── README.md # User documentation
├── CONTRIBUTING.md # This file
└── LICENSE # MIT License
src/analyzers/: Contains all code analysis modules
- Each analyzer implements the
Analyzerinterface - Analyzers run in parallel for performance
- Support incremental analysis for large files
src/engine/: Core analysis engine
- Coordinates analyzer execution
- Manages worker threads
- Handles caching and incremental analysis
src/providers/: VSCode integration
- Implements VSCode provider interfaces
- Handles UI interactions
- Manages diagnostics display
src/ai/: AI service integration
- Abstracts different AI providers
- Handles API communication
- Implements caching and rate limiting
# Update main branch
git checkout main
git pull upstream main
# Create feature branch
git checkout -b feature/my-feature-nameEdit source files in src/ directory:
# Example: Adding a new analyzer
touch src/analyzers/MyAnalyzer.ts
touch src/analyzers/MyAnalyzer.test.tsCompile changes:
npm run compile
# Or use watch mode
npm run watchRun all tests:
npm testRun specific test file:
npm test -- src/analyzers/MyAnalyzer.test.tsRun with coverage:
npm run test:coverageTest in Extension Development Host:
- Press
F5to launch debug mode - Open a test file in the new window
- Verify your changes work as expected
- Check Output panel for logs
Set breakpoints:
- Click in the gutter next to line numbers
- Or use
debugger;statement in code
View logs:
- Output panel: View → Output → CodeGuard
- Debug Console: View → Debug Console
- Developer Tools: Help → Toggle Developer Tools
Common debugging scenarios:
// Add logging
console.log('[CodeGuard] My debug message', data);
// Add breakpoint programmatically
debugger;
// Check analyzer execution
console.log('[Analyzer] Running:', analyzer.name);Stage changes:
git add src/analyzers/MyAnalyzer.ts
git add src/analyzers/MyAnalyzer.test.tsCommit with conventional commit message:
git commit -m "feat(analyzers): add MyAnalyzer for detecting X"# Push to your fork
git push origin feature/my-feature-name
# Create pull request on GitHub
# Include description, screenshots, and testing notesCodeGuard uses a comprehensive testing strategy with both unit tests and property-based tests.
Test specific functionality with concrete examples:
import { describe, it, expect } from 'vitest';
import { SecurityAnalyzer } from './SecurityAnalyzer';
describe('SecurityAnalyzer', () => {
it('should detect SQL injection in string concatenation', () => {
const code = `const query = "SELECT * FROM users WHERE id = " + userId;`;
const analyzer = new SecurityAnalyzer();
const diagnostics = analyzer.analyze({
content: code,
languageId: 'javascript',
fileUri: 'test.js'
});
expect(diagnostics).toHaveLength(1);
expect(diagnostics[0].code).toBe('sql-injection');
expect(diagnostics[0].severity).toBe(DiagnosticSeverity.Error);
});
it('should not flag parameterized queries', () => {
const code = `const query = db.prepare("SELECT * FROM users WHERE id = ?").bind(userId);`;
const analyzer = new SecurityAnalyzer();
const diagnostics = analyzer.analyze({
content: code,
languageId: 'javascript',
fileUri: 'test.js'
});
expect(diagnostics).toHaveLength(0);
});
});Test universal properties across randomized inputs:
import fc from 'fast-check';
import { describe, it } from 'vitest';
describe('Property Tests', () => {
// Feature: codeguard, Property 17: Cache Round-Trip Consistency
it('Property 17: cached results should match fresh analysis', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 10, maxLength: 1000 }),
fc.constantFrom('javascript', 'typescript', 'python'),
async (content, languageId) => {
// First analysis (cache miss)
const result1 = await analysisEngine.analyze({
fileUri: 'test.js',
content,
languageId,
version: 1
});
// Second analysis (cache hit)
const result2 = await analysisEngine.analyze({
fileUri: 'test.js',
content,
languageId,
version: 1
});
// Results should be identical
expect(result2.cacheHit).toBe(true);
expect(result2.diagnostics).toEqual(result1.diagnostics);
}
),
{ numRuns: 100 }
);
});
});All tests:
npm testSpecific test file:
npm test -- src/analyzers/SecurityAnalyzer.test.tsWatch mode (re-run on changes):
npm test -- --watchCoverage report:
npm run test:coverageProperty tests only:
npm test -- --grep "Property"Unit tests only:
npm test -- --grep -v "Property"- Test specific examples: Focus on concrete scenarios
- Test edge cases: Empty inputs, boundary values, error conditions
- Test error handling: Verify graceful degradation
- Use descriptive names: Clearly state what is being tested
- Keep tests focused: One assertion per test when possible
Example:
describe('SecretScanner', () => {
describe('AWS Access Key Detection', () => {
it('should detect valid AWS access key pattern', () => {
const code = 'const key = "AKIAIOSFODNN7EXAMPLE";';
const diagnostics = scanner.analyze({ content: code });
expect(diagnostics).toHaveLength(1);
expect(diagnostics[0].code).toBe('aws-access-key');
});
it('should not flag low-entropy strings', () => {
const code = 'const key = "AKIAAAAAAAAAAAAAAA";';
const diagnostics = scanner.analyze({ content: code });
expect(diagnostics).toHaveLength(0);
});
it('should handle empty input', () => {
const diagnostics = scanner.analyze({ content: '' });
expect(diagnostics).toHaveLength(0);
});
});
});- Test universal properties: Properties that should hold for all inputs
- Use appropriate generators: Create realistic test data
- Run 100+ iterations: Ensure thorough coverage
- Reference design properties: Link to design document
- Handle edge cases: Ensure generators cover boundary conditions
Example:
// Feature: codeguard, Property 6: Secret Detection with Entropy Filtering
it('Property 6: high-entropy secrets detected, low-entropy filtered', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 20, maxLength: 40 }),
async (str) => {
const entropy = calculateEntropy(str);
const code = `const apiKey = "${str}";`;
const diagnostics = await secretScanner.analyze({
content: code,
languageId: 'javascript'
});
// High entropy strings should be flagged
if (entropy > 4.0) {
expect(diagnostics.length).toBeGreaterThan(0);
}
// Low entropy strings should not be flagged
if (entropy < 2.0) {
expect(diagnostics).toHaveLength(0);
}
}
),
{ numRuns: 100 }
);
});- Unit test coverage: >80% for all modules
- Property test coverage: All 39 correctness properties from design
- Integration tests: All VSCode API interactions
- E2E tests: All major user workflows
Run single test:
it.only('should test specific case', () => {
// This test runs alone
});Skip test:
it.skip('should test something', () => {
// This test is skipped
});Debug in VSCode:
- Set breakpoint in test file
- Run
Debug: JavaScript Debug Terminal - Run
npm testin the debug terminal - Debugger stops at breakpoints
- Use strict mode: Enabled in
tsconfig.json - Explicit return types: Always specify function return types
- Avoid
any: Useunknownor proper types instead - Use interfaces: For public APIs and data structures
- Use type aliases: For complex or union types
Good:
interface AnalysisContext {
fileUri: string;
content: string;
languageId: string;
}
function analyze(context: AnalysisContext): Promise<Diagnostic[]> {
// Implementation
}Bad:
function analyze(context: any): any {
// Implementation
}-
Files:
- Classes:
PascalCase.ts(e.g.,SecurityAnalyzer.ts) - Utilities:
camelCase.ts(e.g.,debounce.ts) - Tests:
*.test.tsor*.property.test.ts
- Classes:
-
Classes:
PascalCase(e.g.,SecurityAnalyzer,CacheManager) -
Interfaces:
PascalCasewith descriptive names (e.g.,AnalysisContext,Diagnostic) -
Functions:
camelCase(e.g.,analyzeCode,computeHash) -
Variables:
camelCase(e.g.,fileContent,diagnostics) -
Constants:
UPPER_SNAKE_CASE(e.g.,MAX_CACHE_SIZE,DEFAULT_TIMEOUT) -
Private members: Prefix with
_(e.g.,_cache,_worker)
Example:
const MAX_RETRIES = 3;
interface AnalysisResult {
diagnostics: Diagnostic[];
metadata: AnalysisMetadata;
}
class SecurityAnalyzer implements Analyzer {
private _patterns: VulnerabilityPattern[];
async analyze(context: AnalysisContext): Promise<Diagnostic[]> {
return this._detectVulnerabilities(context);
}
private _detectVulnerabilities(context: AnalysisContext): Diagnostic[] {
// Implementation
}
}Imports: Group and order imports
// 1. Node.js built-ins
import * as fs from 'fs';
import * as path from 'path';
// 2. External dependencies
import * as vscode from 'vscode';
import { LRUCache } from 'lru-cache';
// 3. Internal modules
import { Analyzer, AnalysisContext } from '../types';
import { debounce } from '../utils/debounce';Class structure: Consistent ordering
class MyClass {
// 1. Static properties
static readonly DEFAULT_VALUE = 42;
// 2. Instance properties
private _cache: Cache;
readonly name: string;
// 3. Constructor
constructor(name: string) {
this.name = name;
}
// 4. Public methods
public async analyze(): Promise<void> {
// Implementation
}
// 5. Private methods
private _helper(): void {
// Implementation
}
}Always handle errors gracefully:
async function analyzeFile(uri: string): Promise<Diagnostic[]> {
try {
const content = await fs.promises.readFile(uri, 'utf-8');
return await analyze(content);
} catch (error) {
logger.error('Analysis failed:', error);
// Return empty results, don't crash
return [];
}
}Use custom error types:
class AnalysisError extends Error {
constructor(
message: string,
public readonly analyzer: string,
public readonly cause?: Error
) {
super(message);
this.name = 'AnalysisError';
}
}
throw new AnalysisError('Analysis failed', 'SecurityAnalyzer', originalError);Prefer async/await over promises:
// Good
async function loadData(): Promise<Data> {
const content = await fs.promises.readFile('data.json', 'utf-8');
return JSON.parse(content);
}
// Avoid
function loadData(): Promise<Data> {
return fs.promises.readFile('data.json', 'utf-8')
.then(content => JSON.parse(content));
}Handle concurrent operations:
// Run in parallel
const [result1, result2, result3] = await Promise.all([
analyzer1.analyze(context),
analyzer2.analyze(context),
analyzer3.analyze(context)
]);
// Run sequentially (when order matters)
const result1 = await analyzer1.analyze(context);
const result2 = await analyzer2.analyze(context);
const result3 = await analyzer3.analyze(context);Use ESLint and Prettier:
# Check for issues
npm run lint
# Auto-fix issues
npm run lint -- --fix
# Format code
npm run formatKey formatting rules:
- Indentation: 2 spaces
- Line length: 100 characters max
- Quotes: Single quotes for strings
- Semicolons: Always use semicolons
- Trailing commas: Always in multiline
JSDoc comments for public APIs:
/**
* Analyzes code for security vulnerabilities.
*
* @param context - The analysis context containing file content and metadata
* @returns Array of diagnostics representing detected vulnerabilities
* @throws {AnalysisError} If analysis fails critically
*
* @example
* ```typescript
* const diagnostics = await analyzer.analyze({
* fileUri: 'file.ts',
* content: 'const query = "SELECT * FROM users";',
* languageId: 'typescript'
* });
* ```
*/
async analyze(context: AnalysisContext): Promise<Diagnostic[]> {
// Implementation
}Inline comments for complex logic:
// Use data flow analysis to track tainted variables
// from user input to SQL execution points
const taintedVars = this._trackDataFlow(ast, userInputNodes);
// Only flag as SQL injection if tainted data reaches SQL sink
if (this._isSQLSink(node) && this._isTainted(node, taintedVars)) {
diagnostics.push(this._createDiagnostic(node, 'sql-injection'));
}Checklist:
- Code compiles without errors (
npm run compile) - All tests pass (
npm test) - Test coverage is adequate (>80% for new code)
- Code follows style guidelines (
npm run lint) - Documentation is updated (if needed)
- Commit messages follow conventional commits format
- Branch is up to date with main
-
Push your branch:
git push origin feature/my-feature-name
-
Create PR on GitHub:
- Go to https://github.com/lekhanpro/CodeGuard
- Click "New Pull Request"
- Select your branch
- Fill out the PR template
-
PR Title: Use conventional commit format
feat(security): add XSS detection for React components fix(cache): resolve memory leak in LRU cache docs(readme): update installation instructions -
PR Description: Include:
- What: What changes were made
- Why: Why these changes were needed
- How: How the changes work
- Testing: How you tested the changes
- Screenshots: If UI changes were made
- Breaking Changes: If any (with migration guide)
Example PR Description:
## What
Adds XSS detection for React components by analyzing JSX expressions.
## Why
Current XSS detection only works for vanilla JavaScript. React apps need
specific patterns for detecting unsafe JSX expressions.
## How
- Added JSX parser to SecurityAnalyzer
- Implemented pattern matching for dangerouslySetInnerHTML
- Added data flow analysis for user input in JSX expressions
## Testing
- Added 15 unit tests covering various JSX patterns
- Added property test for JSX expression analysis
- Tested manually with sample React app
## Breaking Changes
None
## Related Issues
Closes #123- Automated Checks: CI runs tests and linting
- Code Review: Maintainers review your code
- Feedback: Address review comments
- Approval: At least one maintainer approval required
- Merge: Maintainer merges your PR
Make changes:
# Make requested changes
git add .
git commit -m "fix: address review feedback"
git push origin feature/my-feature-nameUpdate PR:
- Respond to comments
- Mark conversations as resolved
- Request re-review
Follow Conventional Commits:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changestest: Test additions or changesrefactor: Code refactoringperf: Performance improvementschore: Build process or tooling changesstyle: Code style changes (formatting, etc.)ci: CI/CD changes
Scopes:
security: Security analyzerperformance: Performance analyzersecrets: Secret scannersmells: Code smell detectorai: AI servicecache: Cache managerengine: Analysis engineproviders: VSCode providersconfig: Configuration
Examples:
feat(security): add command injection detection
fix(cache): resolve race condition in LRU eviction
docs(readme): add troubleshooting section
test(analyzer): add property tests for parallel execution
refactor(engine): simplify worker thread management
perf(cache): optimize hash computation
chore(deps): update TypeScript to 5.3Breaking Changes:
feat(api)!: change Analyzer interface signature
BREAKING CHANGE: The analyze() method now requires a version parameter.
Migration: Add version: 1 to all analyze() calls.Analyzers detect specific types of issues in code. Here's how to add one:
Create src/analyzers/MyAnalyzer.ts:
import { Analyzer, AnalysisContext, Diagnostic } from '../types';
import * as vscode from 'vscode';
export class MyAnalyzer implements Analyzer {
readonly name = 'my-analyzer';
readonly supportedLanguages = ['javascript', 'typescript'];
async analyze(context: AnalysisContext): Promise<Diagnostic[]> {
const diagnostics: Diagnostic[] = [];
// Your analysis logic here
const issues = this._detectIssues(context.content);
for (const issue of issues) {
diagnostics.push({
range: new vscode.Range(issue.line, issue.column, issue.line, issue.column + issue.length),
severity: vscode.DiagnosticSeverity.Warning,
message: issue.message,
source: 'codeguard',
code: 'my-rule-id'
});
}
return diagnostics;
}
supportsIncremental(): boolean {
return false; // Set to true if you implement incremental analysis
}
private _detectIssues(content: string): Issue[] {
// Implementation
return [];
}
}
interface Issue {
line: number;
column: number;
length: number;
message: string;
}Add to src/engine/AnalysisEngine.ts:
import { MyAnalyzer } from '../analyzers/MyAnalyzer';
// In constructor or initialization
this.analyzers.push(new MyAnalyzer());Add to package.json:
{
"contributes": {
"configuration": {
"properties": {
"codeguard.analyzers.myAnalyzer.enabled": {
"type": "boolean",
"default": true,
"description": "Enable my analyzer"
}
}
}
}
}Create src/analyzers/MyAnalyzer.test.ts:
import { describe, it, expect } from 'vitest';
import { MyAnalyzer } from './MyAnalyzer';
describe('MyAnalyzer', () => {
const analyzer = new MyAnalyzer();
it('should detect issue X', async () => {
const code = 'const x = problematicPattern();';
const diagnostics = await analyzer.analyze({
fileUri: 'test.js',
content: code,
languageId: 'javascript'
});
expect(diagnostics).toHaveLength(1);
expect(diagnostics[0].code).toBe('my-rule-id');
});
it('should not flag safe patterns', async () => {
const code = 'const x = safePattern();';
const diagnostics = await analyzer.analyze({
fileUri: 'test.js',
content: code,
languageId: 'javascript'
});
expect(diagnostics).toHaveLength(0);
});
});Create src/analyzers/MyAnalyzer.property.test.ts:
import fc from 'fast-check';
import { describe, it } from 'vitest';
import { MyAnalyzer } from './MyAnalyzer';
describe('MyAnalyzer Property Tests', () => {
// Feature: codeguard, Property X: Description
it('Property X: should maintain invariant', async () => {
await fc.assert(
fc.asyncProperty(
fc.string({ minLength: 10, maxLength: 500 }),
async (code) => {
const analyzer = new MyAnalyzer();
const diagnostics = await analyzer.analyze({
fileUri: 'test.js',
content: code,
languageId: 'javascript'
});
// Verify property holds
expect(diagnostics.every(d => d.source === 'codeguard')).toBe(true);
}
),
{ numRuns: 100 }
);
});
});Add to README.md:
- Feature description
- Example detection
- Configuration options
To add support for a new AI service:
Create src/ai/MyAIClient.ts:
import { AIClient, CompletionOptions, RateLimitInfo } from './AIService';
export class MyAIClient implements AIClient {
constructor(
private apiKey: string,
private model: string = 'default-model'
) {}
async complete(prompt: string, options: CompletionOptions): Promise<string> {
try {
const response = await fetch('https://api.myai.com/v1/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: this.model,
prompt,
max_tokens: options.maxTokens,
temperature: options.temperature
})
});
if (!response.ok) {
throw new Error(`API error: ${response.statusText}`);
}
const data = await response.json();
return data.choices[0].text;
} catch (error) {
throw new Error(`MyAI completion failed: ${error.message}`);
}
}
getRateLimitInfo(): RateLimitInfo {
return {
remaining: 100,
reset: Date.now() + 3600000
};
}
}Add to src/ai/AIService.ts:
import { MyAIClient } from './MyAIClient';
private _createClient(): AIClient {
switch (this.config.provider) {
case 'openai':
return new OpenAIClient(this.config.apiKey, this.config.model);
case 'claude':
return new ClaudeClient(this.config.apiKey, this.config.model);
case 'myai':
return new MyAIClient(this.config.apiKey, this.config.model);
default:
throw new Error(`Unknown AI provider: ${this.config.provider}`);
}
}Add to package.json:
{
"contributes": {
"configuration": {
"properties": {
"codeguard.ai.provider": {
"type": "string",
"enum": ["openai", "claude", "myai", "none"],
"default": "none"
}
}
}
}
}Create src/ai/MyAIClient.test.ts:
import { describe, it, expect, vi } from 'vitest';
import { MyAIClient } from './MyAIClient';
describe('MyAIClient', () => {
it('should complete prompts successfully', async () => {
const client = new MyAIClient('test-key', 'test-model');
// Mock fetch
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({
choices: [{ text: 'Fixed code here' }]
})
});
const result = await client.complete('Fix this code', {
maxTokens: 100,
temperature: 0.2
});
expect(result).toBe('Fixed code here');
});
it('should handle API errors', async () => {
const client = new MyAIClient('test-key', 'test-model');
global.fetch = vi.fn().mockResolvedValue({
ok: false,
statusText: 'Unauthorized'
});
await expect(
client.complete('Fix this code', { maxTokens: 100 })
).rejects.toThrow('API error: Unauthorized');
});
});Add to README.md:
- Provider description
- Setup instructions
- Configuration example
- Supported models
To add support for a new programming language:
Add to package.json:
{
"activationEvents": [
"onLanguage:javascript",
"onLanguage:typescript",
"onLanguage:python",
"onLanguage:java",
"onLanguage:go" // New language
]
}Update analyzers to support the new language:
export class SecurityAnalyzer implements Analyzer {
readonly supportedLanguages = [
'javascript',
'typescript',
'python',
'java',
'go' // Add new language
];
private _getPatterns(languageId: string): VulnerabilityPattern[] {
switch (languageId) {
case 'go':
return this._goPatterns;
// ... other languages
}
}
private readonly _goPatterns: VulnerabilityPattern[] = [
{
id: 'sql-injection-go',
pattern: /db\.Query\([^?]*\+/,
severity: DiagnosticSeverity.Error,
message: 'Potential SQL injection in Go'
}
];
}Test language-specific patterns:
describe('SecurityAnalyzer - Go', () => {
it('should detect SQL injection in Go', async () => {
const code = `
query := "SELECT * FROM users WHERE id = " + userId
rows, err := db.Query(query)
`;
const diagnostics = await analyzer.analyze({
fileUri: 'test.go',
content: code,
languageId: 'go'
});
expect(diagnostics).toHaveLength(1);
expect(diagnostics[0].code).toBe('sql-injection-go');
});
});Add to README.md:
- Language in supported languages table
- Language-specific features
- Examples for the new language
CodeGuard must maintain high performance to avoid disrupting developer workflow.
- Analysis time: <100ms for files under 1000 lines
- Activation time: <500ms on VSCode startup
- Memory usage: <5MB when idle, <200MB under load
- Cache operations: <10ms for cache hits
CPU-intensive work should run in worker threads:
// Good: Run in worker thread
const worker = new Worker('./analysis-worker.js');
worker.postMessage({ content, languageId });
// Bad: Block main thread
const result = expensiveAnalysis(content);Cache expensive computations:
class MyAnalyzer {
private _cache = new LRUCache<string, Diagnostic[]>({ max: 1000 });
async analyze(context: AnalysisContext): Promise<Diagnostic[]> {
const hash = this._computeHash(context.content);
// Check cache first
const cached = this._cache.get(hash);
if (cached) return cached;
// Compute and cache
const result = await this._doAnalysis(context);
this._cache.set(hash, result);
return result;
}
}For large files, analyze only changed regions:
async analyzeIncremental(
context: IncrementalAnalysisContext
): Promise<Diagnostic[]> {
// Only analyze affected ranges
const affectedDiagnostics = [];
for (const range of context.affectedRanges) {
const regionDiagnostics = await this._analyzeRegion(
context.content,
range
);
affectedDiagnostics.push(...regionDiagnostics);
}
// Merge with cached diagnostics from unchanged regions
return this._mergeDiagnostics(
affectedDiagnostics,
context.cachedDiagnostics,
context.affectedRanges
);
}Profile and optimize frequently-called code:
// Bad: Regex compilation in loop
for (const line of lines) {
if (/pattern/.test(line)) {
// ...
}
}
// Good: Compile regex once
const pattern = /pattern/;
for (const line of lines) {
if (pattern.test(line)) {
// ...
}
}Batch multiple operations together:
// Bad: Multiple individual operations
for (const file of files) {
await analyzeFile(file);
}
// Good: Batch with Promise.all
await Promise.all(files.map(file => analyzeFile(file)));Create benchmarks for critical paths:
import { describe, bench } from 'vitest';
describe('SecurityAnalyzer Performance', () => {
bench('analyze 100-line file', async () => {
const code = generateCode(100);
await analyzer.analyze({ content: code, languageId: 'javascript' });
});
bench('analyze 1000-line file', async () => {
const code = generateCode(1000);
await analyzer.analyze({ content: code, languageId: 'javascript' });
});
bench('cache hit', async () => {
const code = 'const x = 1;';
await analyzer.analyze({ content: code, languageId: 'javascript' });
// Second call should hit cache
await analyzer.analyze({ content: code, languageId: 'javascript' });
});
});Use VSCode's built-in profiler:
- Open Command Palette
- Run "Developer: Show Running Extensions"
- Click "Profile" next to CodeGuard
- Perform actions to profile
- Stop profiling and analyze results
Monitor memory usage:
function logMemoryUsage() {
const usage = process.memoryUsage();
console.log({
rss: `${Math.round(usage.rss / 1024 / 1024)}MB`,
heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)}MB`,
heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`
});
}Before submitting performance-sensitive code:
- Profiled with realistic data
- Benchmarked critical paths
- Verified memory usage is acceptable
- Tested with large files (>5000 lines)
- Verified cache effectiveness
- Checked for memory leaks
- Ensured non-blocking execution
Update documentation when:
- Adding features: Document new functionality
- Changing APIs: Update API documentation
- Modifying configuration: Update configuration reference
- Fixing bugs: Update troubleshooting guide if relevant
- Changing behavior: Update user guide
-
README.md: User-facing documentation
- Features and capabilities
- Installation instructions
- Usage examples
- Configuration options
- Troubleshooting
-
CONTRIBUTING.md: Developer documentation (this file)
- Development setup
- Code style guidelines
- Testing requirements
- Pull request process
-
API.md: API documentation
- Public interfaces
- Extension points
- Integration guide
-
CHANGELOG.md: Version history
- New features
- Bug fixes
- Breaking changes
- Migration guides
Be clear and concise:
<!-- Good -->
## Installation
Install from the VSCode Marketplace:
1. Open Extensions (Ctrl+Shift+X)
2. Search for "CodeGuard"
3. Click Install
<!-- Bad -->
## Installation
You can install this extension by opening the Extensions view in VSCode
which can be accessed by pressing Ctrl+Shift+X on Windows/Linux or
Cmd+Shift+X on Mac, then typing "CodeGuard" into the search box...Use examples:
<!-- Good -->
### Configuration
Enable AI features:
```json
{
"codeguard.ai.enabled": true,
"codeguard.ai.provider": "ollama"
}You can enable AI features by setting the appropriate configuration values.
**Include screenshots** for UI features:
```markdown

Use realistic examples:
// Good: Realistic example
const query = db.prepare("SELECT * FROM users WHERE id = ?").bind(userId);
// Bad: Trivial example
const x = 1;Show before and after:
// Before (with issue)
const query = "SELECT * FROM users WHERE id = " + userId;
// After (fixed)
const query = db.prepare("SELECT * FROM users WHERE id = ?").bind(userId);- Questions: Open a Discussion
- Bugs: Open an Issue
- Features: Open a Feature Request
- Security: Report via GitHub Security
- GitHub: https://github.com/lekhanpro/CodeGuard
- Discussions: https://github.com/lekhanpro/CodeGuard/discussions
By contributing to CodeGuard, you agree that your contributions will be licensed under the MIT License.
Thank you for contributing to CodeGuard! Your efforts help make code quality tools accessible to all developers.
Happy coding! 🚀