This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
IMPORTANT: Use Latest LTS Versions
This project requires the latest LTS versions for optimal performance and security:
- Node.js: v22.x (Current LTS)
- pnpm: v10.13.1 (Latest version)
Install/Update pnpm:
# Update to latest version
pnpm self-update
# Or install globally
npm install -g pnpm@latestThe project will enforce these versions through:
enginesfield in package.jsonengine-strict=truein .npmrc
Firebase Configuration
The project includes a committed development environment configuration at apps/webapp/.env.development with safe test values that work out of the box with Firebase emulators. No additional setup is required for local development.
For production deployments:
- Firebase App Hosting configurations are defined in
apphosting.yaml(base),apphosting.staging.yaml(staging overrides), andapphosting.prod.yaml(production overrides) - The automatically generated
FIREBASE_WEBAPP_CONFIGis not used - the app uses individualNEXT_PUBLIC_FIREBASE_*variables - as defined in apphosting.yaml - for better control and debugging.
Get Firebase Configuration:
- Visit Firebase Console
- Select your project → Project Settings → General tab
- Scroll to "Your apps" section and copy the config values
Environment File Structure:
The project uses a two-tier environment configuration:
.env: Base configuration with safe defaults for all Firebase variables. These values are used during builds and as fallbacks..env.development: Development-specific overrides containing only:- Firebase emulator configuration (
FIREBASE_*_EMULATOR_HOST) - Debug logging level (
NEXT_PUBLIC_AUTH_LOG_LEVEL=debug)
- Firebase emulator configuration (
This eliminates duplication while maintaining clear separation between base defaults and development-specific settings.
This monorepo REQUIRES ESNext modules ("module": "ESNext") throughout all TypeScript configurations. This is not optional and is critical for several reasons:
-
🔥 Firebase OAuth Authentication WILL BREAK if compiled to CommonJS
- Firebase SDK requires direct object references for OAuth URL generation
- CommonJS compilation creates indirect references that corrupt the SDK's internal state
- Symptom: OAuth URLs missing
&providerId=google.comparameters, preventing successful authentication - Root Cause:
require()creates proxy objects that break Firebase's internal provider registration
-
🚀 Modern JavaScript Standard: ESM is the official module system for modern JavaScript
-
📦 Better Tree Shaking: Improved bundle optimization and smaller builds
-
⚡ Performance: Parallel module loading vs synchronous CommonJS
✅ All packages successfully migrated to ESM:
packages/domain- Core business logic with decorators ✅packages/ui- React components ✅packages/test-utils- Testing utilities ✅packages/infrastructure- Firebase integration ✅packages/config-typescript- TypeScript configurations ✅- All applications ✅
❌ DON'T override module settings in TypeScript configs:
// DON'T DO THIS - Will break Firebase OAuth
{
"compilerOptions": {
"module": "commonjs", // ← BREAKS Firebase OAuth
"moduleResolution": "node" // ← BREAKS Firebase OAuth
}
}❌ DON'T remove "type": "module" from package.json files
❌ DON'T use require() or module.exports in source code
❌ DON'T use CommonJS-style imports anywhere in the codebase
When creating new packages or files, always:
-
Package.json MUST include:
{ "type": "module" } -
TypeScript configs MUST:
- Extend from
@decision-copilot/config-typescriptconfigurations - Never override
moduleormoduleResolutionsettings from base config
- Extend from
-
Use ES6 imports/exports only:
// ✅ Good - ES6 imports import { something } from './module'; export { something }; export default MyComponent; // ❌ Bad - CommonJS (don't use) const something = require('./module'); module.exports = something;
The monorepo includes multiple layers of protection against CommonJS regression:
- Documentation: This file and
packages/config-typescript/README.mdcontain warnings - Automated Tests: Config validation ensures
"module": "ESNext"(coming in Phase 2) - Pre-commit Hooks: Prevent CommonJS code from being committed (coming in Phase 3)
- ESLint Rules: Enforce ES6 import syntax (coming in Phase 3)
If you see Firebase OAuth errors such as:
- OAuth URLs missing
&providerId=google.comparameters - Authentication popup redirects but doesn't complete sign-in
- Console errors about provider configuration
Check for CommonJS regression:
# 1. Verify all TypeScript configs use ESNext
grep -r '"module"' packages/config-typescript/
# Should only show "ESNext", never "commonjs"
# 2. Verify all packages have "type": "module"
find packages/ -name "package.json" -exec grep -l '"type": "module"' {} \;
# 3. Check for CommonJS syntax in source code
grep -r "require(" src/ packages/ apps/ || echo "No CommonJS found ✅"
grep -r "module.exports" src/ packages/ apps/ || echo "No CommonJS found ✅"Emergency fix if CommonJS found:
- Check
packages/config-typescript/base.jsonhas"module": "ESNext" - Ensure no TypeScript config overrides this setting
- Verify all package.json files have
"type": "module" - Replace any
require()with ES6importstatements - Run
pnpm run buildto recompile with ESM
IMPORTANT: Initial Setup
After cloning or installing dependencies, you must build packages first:
pnpm run buildThis builds all workspace packages in dependency order. Required before first pnpm run dev to generate package outputs that other packages import.
When to rebuild:
- ✅ Not needed for regular code changes (watch mode handles this)
- ❌ Required after:
- Fresh clone/install
- Adding/removing dependencies
- Changing package.json exports
- Modifying TypeScript configs
Start development environment:
pnpm run devThis starts Firebase emulators with data import and Next.js dev server with Turbopack.
Build and test before push:
pnpm run pre:pushRuns lint, unit tests, and build - required before pushing.
Testing:
pnpm run test:unit # Unit tests only (excludes integration tests)
pnpm run test:unit:watch # Watch mode for unit tests
pnpm run test # All tests including integrationDevelopment Logs:
# View latest development logs (timestamped, hourly rotation)
ls -la logs/turbo-dev.*.log | tail -1 # Find latest log file
tail -f logs/turbo-dev.$(date '+%Y-%m-%d-%H').log # Follow current hour's log
cat logs/turbo-dev.$(date '+%Y-%m-%d-%H').log # Read current hour's log
# Examples:
tail -100 logs/turbo-dev.2025-07-13-14.log # Last 100 lines from 2 PM session
grep -i error logs/turbo-dev.*.log # Search for errors across all logsLinting:
pnpm run lint # Check for lint errors
pnpm run lint:fix # Auto-fix lint errorsFirebase:
pnpm run dev:emulators:with-data # Start emulators with imported data
pnpm run emulators:export # Export emulator dataTypeScript Declaration Files Missing: If you see "Cannot find module" errors for workspace packages, or .d.ts files are missing:
# Clear corrupted TypeScript build cache
find . -name "*.tsbuildinfo" -exec rm {} \;
pnpm run buildTurbo Cache Issues: If builds are inconsistent or failing unexpectedly:
# Full clean including all caches
pnpm run clean
pnpm run buildModule Import Failures: If workspace packages can't import each other:
# Ensure all packages are built first
pnpm run build
# Check that lib/ directories contain .d.ts files
ls packages/domain/lib/*.d.tsThis is a Next.js 15 + React 19 application using Firebase for backend services. The codebase follows a layered architecture with domain-driven design principles.
- Presentation (
app/): Next.js App Router pages and layouts - Components (
components/): Reusable UI components using shadcn/ui + Radix UI - Application (
hooks/): Custom React hooks for business logic and data fetching - Domain (
lib/domain/): Core business models, validation, and repository interfaces - Infrastructure (
lib/infrastructure/): Firestore repository implementations
Domain objects use Props interfaces with class-validator decorators:
interface DecisionProps {
id: string
title: string
// ... other props
}
class Decision {
@IsString()
@MinLength(5)
readonly title: string
private constructor(props: DecisionProps) { /* ... */ }
static create(props: DecisionProps): Decision { /* ... */ }
}Key patterns:
- Props interfaces define data structure
- Domain classes contain validation and business logic
- Private constructors with static factory methods
- Immutable properties
- Repository pattern for data access
- Framework: Next.js 15 with App Router
- Database: Firebase Firestore
- Auth: Firebase Auth
- UI: shadcn/ui components with Radix UI primitives
- Styling: Tailwind CSS
- Forms: React Hook Form with class-validator
- Testing: Vitest
- Package Manager: pnpm
Use absolute imports with @/ prefix:
import { Decision } from '@/lib/domain/Decision'
import { useDecisions } from '@/hooks/useDecisions'- Development uses Firebase emulators with local data import
- Firestore repositories implement domain repository interfaces
- Authentication handled through Firebase Auth
- Unit tests exclude integration tests (
**/*.integration.test.ts) - Integration tests require Firebase emulators
- Domain objects have comprehensive validation tests
- Repository pattern enables easy mocking for tests
CRITICAL: Always test sed patterns thoroughly before applying to multiple files
Common pitfalls and solutions:
-
Quote Type Handling: When replacing import paths, account for both single and double quotes:
# ❌ WRONG: Only handles double quotes sed 's|@old-package/[^"]*|@new-package|g' # ✅ CORRECT: Handles both quote types sed "s#@old-package/[^'\"]*#@new-package#g"
-
Delimiter Choice: Use
#delimiter when pattern contains/or|:# ❌ WRONG: Conflicts with path separators sed 's|@package/[^"]*|@new|g' # ✅ CORRECT: Use # to avoid conflicts sed 's#@package/[^"]*#@new#g'
-
Always Test First: Test patterns on sample text before applying to files:
# Test your pattern first echo 'import { Test } from "@old-package/module";' | sed 's#pattern#replacement#g' # Then apply to files find . -name "*.ts" -exec sed -i 's#pattern#replacement#g' {} \;
-
Character Class Escaping: When matching until quotes, escape properly:
# ✅ CORRECT: Escape quotes in character class sed "s#@package/[^'\"]*#@new#g"
CRITICAL: Always use pnpm exec instead of npx for project tools
This project uses pnpm workspaces and specific tool versions. Always use pnpm exec to ensure you're using the project's installed versions:
# ❌ WRONG: Uses global or system version
npx tsc --project tsconfig.build.json
npx vitest run
npx eslint src/
# ✅ CORRECT: Uses project's installed version
pnpm exec tsc --project tsconfig.build.json
pnpm exec vitest run
pnpm exec eslint src/
# ✅ EVEN BETTER: Use package.json scripts when available
pnpm run build
pnpm run test
pnpm run lintWhy this matters:
- Ensures consistent tool versions across the team
- Uses the exact TypeScript/ESLint/Vitest versions specified in package.json
- Avoids version conflicts and unexpected behavior
- Respects workspace dependencies and configurations
- Vitest Best Practices:
- Use pnpm rather than npx when running vitest