This guide covers daily development practices and common workflows in the Claude Flow monorepo.
We use explicit task files instead of command-line arguments for better discoverability and type safety.
# Building
pnpm build # Standard development build
pnpm build:prod # Optimized production build
# Testing
pnpm test # Run tests once
pnpm test:coverage # Generate coverage report
pnpm test:watch # Watch mode for TDD
# Code Quality
pnpm lint # Check for linting issues
pnpm lint:fix # Auto-fix linting issues
pnpm typecheck # TypeScript type checking
# Development
pnpm dev # Start dev server with hot reload
pnpm clean # Remove build artifactsInstead of:
# Old approach - hard to discover options
repo-scripts test --coverage --watch --reporter=verboseWe use:
# New approach - each variant is discoverable
pnpm test:coverage
pnpm test:watchBenefits:
- All commands visible in package.json
- Each task file can have specific TypeScript types
- No complex argument parsing needed
- Better documentation per task
Packages can opt-in to specific tools by adding configuration files.
packages/my-app/
├── src/
├── package.json
├── tsconfig.json # Extends base, enables TypeScript
├── .eslintrc.js # Extends base, enables linting
└── vitest.config.ts # Enables testing for this packageRepo scripts behavior:
- ✅
tsconfig.jsonexists → TypeScript runs - ✅
.eslintrc.jsexists → ESLint runs - ✅
vitest.config.tsexists → Tests run - ❌ No config → Tool skips this package (no-op)
TypeScript (tsconfig.json):
{
"extends": "@claude-flow/tsconfig/react.json",
"compilerOptions": {
"outDir": "./dist"
},
"include": ["src"],
"references": [{ "path": "../../packages/design-system" }]
}ESLint (.eslintrc.js):
module.exports = {
extends: ['@claude-flow/eslint-config/react'],
rules: {
// Package-specific overrides
'react/prop-types': 'off',
},
};Vitest (vitest.config.ts):
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
},
});-
Create a feature branch
git checkout -b feature/my-new-feature
-
Scaffold if needed
# Need a new component? pnpm scaffold component MyComponent --package design-system -
Develop with hot reload
pnpm dev
-
Write tests alongside code
pnpm test:watch
-
Check your work
pnpm lint:fix pnpm typecheck pnpm test
When your feature spans multiple packages:
-
Make changes in dependency order
- First: shared types
- Second: utility packages
- Third: UI components
- Last: applications
-
Use workspace commands
# Build all affected packages pnpm build --filter ...my-app # Test a specific package pnpm test --filter @claude-flow/design-system
-
Verify with E2E tests
cd apps/e2e pnpm test
-
Browser DevTools
- Use debugger statements
- React Developer Tools
- Network tab for API calls
-
VS Code Debugging
{ "type": "chrome", "request": "launch", "name": "Debug V2 Web", "url": "http://localhost:4000", "webRoot": "${workspaceFolder}/apps/v2/web" }
-
Console Logging
console.log('Debug info:', { data });
-
VS Code Debugging
{ "type": "node", "request": "launch", "name": "Debug V2 Server", "runtimeExecutable": "pnpm", "runtimeArgs": ["run", "dev"], "cwd": "${workspaceFolder}/apps/v2/server" }
# See all type errors
pnpm typecheck
# Check a specific package
cd packages/my-package
pnpm typecheck
# Generate declaration files
pnpm build --declarationWrite tests next to the code:
src/
├── Button.tsx
├── Button.test.tsx # Unit tests
└── Button.stories.tsx # Visual tests
Run tests:
pnpm test # Single run
pnpm test:watch # Watch mode
pnpm test:coverage # Coverage reportFor API endpoints and services:
// server/routes/api.test.ts
import { createTestClient } from '../test/utils';
test('GET /api/users returns users', async () => {
const client = createTestClient();
const response = await client.get('/api/users');
expect(response.status).toBe(200);
});Located in apps/e2e/:
// apps/e2e/tests/login.spec.ts
import { test, expect } from '@playwright/test';
test('user can log in', async ({ page }) => {
await page.goto('/');
await page.fill('[name="email"]', 'user@example.com');
await page.fill('[name="password"]', 'password');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
});-
Use Lage caching
# First build is slow pnpm build # Subsequent builds are fast (cached) pnpm build
-
Build specific packages
# Only build what changed pnpm build --filter my-package -
Clean when needed
# If cache is stale pnpm clean pnpm build
-
Use project references
- Faster TypeScript checking
- Incremental compilation
-
Optimize imports
// ❌ Slow - imports entire library import _ from 'lodash'; // ✅ Fast - imports only what's needed import debounce from 'lodash/debounce';
Before committing:
# Fix formatting
pnpm lint:fix
# Check types
pnpm typecheck
# Run tests
pnpm test- Tests added/updated
- TypeScript types correct
- No console.log left
- Documentation updated
- Follows naming conventions
- No hardcoded values
# Clear all caches
pnpm clean
rm -rf node_modules
pnpm install
# Update dependencies
pnpm update --interactive# Rebuild packages in order
pnpm build --filter '@claude-flow/*'
# Check for circular dependencies
pnpm why <package-name># Run specific test
pnpm test -- Button.test.tsx
# Debug mode
pnpm test:debug
# Update snapshots
pnpm test -- -u- ✅ Write tests for new features
- ✅ Use TypeScript types (avoid
any) - ✅ Follow existing patterns
- ✅ Clean up console.logs
- ✅ Update documentation
- ❌ Commit directly to main
- ❌ Skip tests to save time
- ❌ Use relative imports across packages
- ❌ Ignore TypeScript errors
- ❌ Leave TODO comments without tickets
A typical day might look like:
# Morning - sync with latest
git pull origin main
pnpm install
# Start development
pnpm dev
# Make changes, test as you go
pnpm test:watch
# Before lunch - checkpoint
pnpm lint:fix
pnpm typecheck
git add -A
git commit -m "feat: work in progress"
# After lunch - continue development
# ... more changes ...
# End of day - clean up and push
pnpm test
pnpm build
git push origin feature/my-feature
# Create PR for review