Skip to content

Latest commit

 

History

History
478 lines (365 loc) · 10.3 KB

File metadata and controls

478 lines (365 loc) · 10.3 KB

Contributing to StackFast

Thank you for your interest in contributing to StackFast! This document provides guidelines and instructions for contributing.

Code of Conduct

Be respectful, inclusive, and constructive in all interactions.

Getting Started

  1. Fork the repository
  2. Clone your fork: git clone https://github.com/yourusername/stackfast.git
  3. Install dependencies: npm install
  4. Create a branch: git checkout -b feature/your-feature-name

Development Workflow

1. Make Your Changes

Follow the project standards documented in .kiro/steering/stackfast-standards.md:

  • Use TypeScript strictly (no any types)
  • Follow WCAG 2.1 AA accessibility guidelines
  • Write tests for new features
  • Keep functions under 50 lines
  • Use descriptive variable names

2. Test Your Changes

# Run all tests
npm test

# Run type checking
npm run type-check

# Run linting
npm run lint

# Test in browser
npm run dev

3. Commit Your Changes

Use clear, descriptive commit messages:

git commit -m "feat: add support for new database tool"
git commit -m "fix: resolve worker timeout issue"
git commit -m "docs: update catalog update process"

4. Push and Create Pull Request

git push origin feature/your-feature-name

Then open a Pull Request on GitHub with:

  • Clear description of changes
  • Link to related issues
  • Screenshots for UI changes
  • Test results

Adding New Tools

1. Update Tool Catalog

Add your tool to public/catalog/v1/tools.json:

{
  "id": "my-tool",
  "name": "My Tool",
  "categoryId": "database",
  "description": "A great database for modern apps",
  "languages": ["javascript", "typescript"],
  "supports": {
    "runtime": ["node", "bun"],
    "dbs": [],
    "frameworks": []
  },
  "integrations": ["prisma", "drizzle"],
  "hosted": true,
  "selfHostable": false,
  "pricing": {
    "model": "free-tier",
    "note": "Free up to 1GB",
    "url": "https://example.com/pricing",
    "lastVerified": "2024-01-15"
  },
  "docsUrl": "https://example.com/docs"
}

Required Fields:

  • id: Unique identifier (kebab-case)
  • name: Display name
  • categoryId: One of the canonical category IDs
  • description: Brief description (1-2 sentences)
  • languages: Supported languages
  • supports: Compatibility information
  • integrations: Array of tool IDs this integrates with

Optional Fields:

  • hosted: Boolean for hosted services
  • selfHostable: Boolean for self-hosting option
  • pricing: Pricing information
  • docsUrl: Link to documentation

2. Add Compatibility Rules

Add rules to public/catalog/v1/rules.json:

{
  "id": "my-tool-synergy-prisma",
  "version": "1.0.0",
  "kind": "synergy",
  "toolA": "my-tool",
  "toolB": "prisma",
  "reason": "First-class integration with Prisma ORM",
  "weight": 8
}

Rule Types:

  • conflict: Tools that don't work together (negative weight)
  • synergy: Tools that work great together (positive weight)
  • requirement: Tool A requires Tool B
  • capabilityCompat: Compatibility based on capabilities
  • categoryCoverage: Bonus for filling required categories

Weight Guidelines:

  • Conflicts: -15 to -5
  • Synergies: +3 to +10
  • Requirements: 0 (just diagnostic)
  • Capability compat: +2 to +5 (capped at +12 total)

3. Create Export Recipe

Create src/data/recipes/my-tool.ts:

import type { ExportRecipe } from '@/types';

export const myToolRecipe: ExportRecipe = {
  id: 'my-tool',
  version: '1.0.0',
  appliesWhen: (tools) => tools.some(t => t.id === 'my-tool'),
  targets: {
    packageJson: {
      deps: {
        'my-tool-client': '^1.0.0',
      },
      devDeps: {
        '@types/my-tool': '^1.0.0',
      },
      scripts: {
        'db:start': 'my-tool start',
        'db:migrate': 'my-tool migrate',
      },
    },
    files: [
      {
        path: 'my-tool.config.js',
        templateId: 'my-tool-config',
        mergeStrategy: 'create',
      },
    ],
    env: {
      example: {
        MY_TOOL_URL: 'postgresql://localhost:5432/mydb',
        MY_TOOL_API_KEY: 'your-api-key-here',
      },
    },
    postInstallSteps: [
      'Sign up at https://example.com',
      'Copy your API key to .env',
      'Run `npm run db:migrate` to set up database',
    ],
  },
  conflicts: [], // Optional: recipe IDs that conflict
};

4. Register Recipe

Add to src/data/recipes/index.ts:

import { myToolRecipe } from './my-tool';

export const recipes: ExportRecipe[] = [
  // ... existing recipes
  myToolRecipe,
];

5. Create Template (if needed)

If your recipe needs a config file, create src/templates/config-files/my-tool-config.ts:

import type { Tool } from '@/types';

export function generateMyToolConfig(tools: Tool[]): string {
  return `// My Tool Configuration
module.exports = {
  // Configuration here
};
`;
}

Register in src/templates/config-files/index.ts.

6. Update Manifest

Update public/catalog/v1/manifest.json:

{
  "version": "1.1.0",
  "updatedAt": "2024-01-15T12:00:00Z",
  "files": {
    "categories": "/catalog/v1/categories.json",
    "tools": "/catalog/v1/tools.json",
    "rules": "/catalog/v1/rules.json"
  },
  "etag": "new-unique-hash"
}

7. Test Your Tool

# Run tests
npm test

# Test in browser
npm run dev

# Select your tool and verify:
# - Tool appears in correct category
# - Compatibility rules work
# - Export generates correct files
# - No console errors

Testing Guidelines

Unit Tests

Test individual functions in isolation:

import { describe, it, expect } from 'vitest';
import { myFunction } from './my-module';

describe('myFunction', () => {
  it('should handle valid input', () => {
    expect(myFunction('valid')).toBe('expected');
  });

  it('should handle edge cases', () => {
    expect(myFunction('')).toBe('default');
  });
});

Integration Tests

Test component interactions:

import { render, screen, fireEvent } from '@testing-library/react';
import { MyComponent } from './MyComponent';

it('should update when button clicked', () => {
  render(<MyComponent />);
  fireEvent.click(screen.getByRole('button'));
  expect(screen.getByText('Updated')).toBeInTheDocument();
});

Snapshot Tests

Test export outputs:

import { generateExport } from '@/lib/export-generator';

it('should generate consistent package.json', async () => {
  const result = await generateExport(tools, diagnostics, 'zip');
  const packageJson = result.files.find(f => f.path === 'package.json');
  expect(JSON.parse(packageJson.content)).toMatchSnapshot();
});

Code Style

TypeScript

// ✅ Good
function calculateScore(diagnostics: Diagnostic[]): number {
  const bonuses = diagnostics.filter(d => (d.weight ?? 0) > 0);
  return Math.min(sum(bonuses), 40);
}

// ❌ Bad
function calculateScore(diagnostics: any) {
  return diagnostics.reduce((a, b) => a + b.weight, 0);
}

React Components

// ✅ Good
interface MyComponentProps {
  title: string;
  onClose: () => void;
}

export function MyComponent({ title, onClose }: MyComponentProps) {
  return <div>{title}</div>;
}

// ❌ Bad
export default function MyComponent(props) {
  return <div>{props.title}</div>;
}

Naming Conventions

  • Components: PascalCase (ToolSelector)
  • Functions: camelCase (calculateScore)
  • Constants: UPPER_SNAKE_CASE (MAX_SCORE)
  • Files: kebab-case (tool-selector.tsx)
  • Types: PascalCase (ToolId, CategoryId)

Documentation

JSDoc Comments

Add JSDoc for all exported functions:

/**
 * Calculate compatibility score from diagnostics
 * 
 * @param diagnostics - Array of diagnostic results
 * @returns Score between 0 and 100
 */
export function calculateScore(diagnostics: Diagnostic[]): number {
  // Implementation
}

Inline Comments

Explain "why", not "what":

// ✅ Good
// Cap capability bonuses to prevent score inflation
const cappedBonuses = Math.min(capabilityBonuses, 12);

// ❌ Bad
// Set cappedBonuses to minimum of capabilityBonuses and 12
const cappedBonuses = Math.min(capabilityBonuses, 12);

Pull Request Guidelines

PR Title

Use conventional commit format:

  • feat: add PostgreSQL support
  • fix: resolve worker timeout on mobile
  • docs: update catalog update process
  • test: add tests for rules engine
  • refactor: simplify score calculation
  • perf: optimize rule evaluation

PR Description

Include:

  1. What: What changes were made
  2. Why: Why these changes were needed
  3. How: How the changes work
  4. Testing: How you tested the changes
  5. Screenshots: For UI changes

Example:

## What
Adds support for PostgreSQL database with Prisma integration

## Why
PostgreSQL is a popular choice for full-stack apps and was requested by users

## How
- Added PostgreSQL to tools.json
- Created compatibility rules with Prisma and Drizzle
- Added export recipe with connection string setup
- Created config template

## Testing
- ✅ Unit tests pass
- ✅ Integration tests pass
- ✅ Manual testing in browser
- ✅ Export generates correct files

## Screenshots
[Screenshot of tool selection]
[Screenshot of export output]

Review Process

  1. Automated checks must pass (tests, type check, lint)
  2. At least one maintainer approval required
  3. Address review feedback
  4. Squash commits before merge

Common Issues

TypeScript Errors

Issue: Property 'dbs' does not exist on type 'Supports'

Solution: Check if property exists before accessing:

if (Array.isArray(tool.supports?.dbs)) {
  // Use tool.supports.dbs
}

Worker Errors

Issue: Worker is not defined

Solution: Guard worker creation:

if (typeof window !== 'undefined' && 'Worker' in window) {
  worker = new Worker(url);
}

Test Failures

Issue: Tests fail with "Cannot find module"

Solution: Check tsconfig.json path aliases match test setup

Getting Help

Recognition

Contributors will be:

  • Listed in README
  • Mentioned in release notes
  • Credited in commit history

Thank you for contributing to StackFast! 🚀