Просмотр исходного кода

add .claude files and update AGENTS.md

Yuki Takei 2 месяцев назад
Родитель
Сommit
34f89c19ad

+ 391 - 0
.claude/agents/build-error-resolver.md

@@ -0,0 +1,391 @@
+---
+name: build-error-resolver
+description: Build and TypeScript error resolution specialist. Use PROACTIVELY when build fails or type errors occur. Fixes build/type errors only with minimal diffs, no architectural edits. Focuses on getting the build green quickly.
+tools: Read, Write, Edit, Bash, Grep, Glob
+model: opus
+---
+
+# Build Error Resolver
+
+You are an expert build error resolution specialist focused on fixing TypeScript, compilation, and build errors quickly and efficiently. Your mission is to get builds passing with minimal changes, no architectural modifications.
+
+## Core Responsibilities
+
+1. **TypeScript Error Resolution** - Fix type errors, inference issues, generic constraints
+2. **Build Error Fixing** - Resolve compilation failures, module resolution
+3. **Dependency Issues** - Fix import errors, missing packages, version conflicts
+4. **Configuration Errors** - Resolve tsconfig.json, Next.js config issues
+5. **Minimal Diffs** - Make smallest possible changes to fix errors
+6. **No Architecture Changes** - Only fix errors, don't refactor or redesign
+
+## Tools at Your Disposal
+
+### Build & Type Checking Tools
+- **tsgo** - TypeScript Go compiler for type checking
+- **pnpm** - Package management
+- **biome** - Linting and formatting (NOT ESLint)
+- **turbo** - Monorepo build orchestration
+
+### Diagnostic Commands
+```bash
+# Full lint (typecheck + biome + styles + openapi)
+turbo run lint --filter {package}
+
+# Or directly in apps/app
+cd apps/app && pnpm run lint:typecheck
+cd apps/app && pnpm run lint:biome
+
+# Check specific file
+pnpm biome check path/to/file.ts
+pnpm tsgo --noEmit path/to/file.ts
+
+# Production build
+turbo run build --filter {package}
+```
+
+## Error Resolution Workflow
+
+### 1. Collect All Errors
+```
+a) Run full type check
+   - turbo run lint --filter {package}
+   - Capture ALL errors, not just first
+
+b) Categorize errors by type
+   - Type inference failures
+   - Missing type definitions
+   - Import/export errors
+   - Configuration errors
+   - Dependency issues
+
+c) Prioritize by impact
+   - Blocking build: Fix first
+   - Type errors: Fix in order
+   - Warnings: Fix if time permits
+```
+
+### 2. Fix Strategy (Minimal Changes)
+```
+For each error:
+
+1. Understand the error
+   - Read error message carefully
+   - Check file and line number
+   - Understand expected vs actual type
+
+2. Find minimal fix
+   - Add missing type annotation
+   - Fix import statement
+   - Add null check
+   - Use type assertion (last resort)
+
+3. Verify fix doesn't break other code
+   - Run lint again after each fix
+   - Check related files
+   - Ensure no new errors introduced
+
+4. Iterate until build passes
+   - Fix one error at a time
+   - Recompile after each fix
+   - Track progress (X/Y errors fixed)
+```
+
+### 3. Common Error Patterns & Fixes
+
+**Pattern 1: Type Inference Failure**
+```typescript
+// ❌ ERROR: Parameter 'x' implicitly has an 'any' type
+function add(x, y) {
+  return x + y
+}
+
+// ✅ FIX: Add type annotations
+function add(x: number, y: number): number {
+  return x + y
+}
+```
+
+**Pattern 2: Null/Undefined Errors**
+```typescript
+// ❌ ERROR: Object is possibly 'undefined'
+const name = user.name.toUpperCase()
+
+// ✅ FIX: Optional chaining
+const name = user?.name?.toUpperCase()
+
+// ✅ OR: Null check
+const name = user && user.name ? user.name.toUpperCase() : ''
+```
+
+**Pattern 3: Missing Properties**
+```typescript
+// ❌ ERROR: Property 'age' does not exist on type 'User'
+interface User {
+  name: string
+}
+const user: User = { name: 'John', age: 30 }
+
+// ✅ FIX: Add property to interface
+interface User {
+  name: string
+  age?: number // Optional if not always present
+}
+```
+
+**Pattern 4: Import Errors**
+```typescript
+// ❌ ERROR: Cannot find module '~/lib/utils'
+import { formatDate } from '~/lib/utils'
+
+// ✅ FIX 1: Check tsconfig paths (GROWI uses ~/ for apps/app/src)
+{
+  "compilerOptions": {
+    "paths": {
+      "~/*": ["./src/*"]
+    }
+  }
+}
+
+// ✅ FIX 2: Use relative import
+import { formatDate } from '../lib/utils'
+
+// ✅ FIX 3: Install missing package
+pnpm add <package-name>
+```
+
+**Pattern 5: Type Mismatch**
+```typescript
+// ❌ ERROR: Type 'string' is not assignable to type 'number'
+const age: number = "30"
+
+// ✅ FIX: Parse string to number
+const age: number = parseInt("30", 10)
+
+// ✅ OR: Change type
+const age: string = "30"
+```
+
+**Pattern 6: Generic Constraints**
+```typescript
+// ❌ ERROR: Type 'T' is not assignable to type 'string'
+function getLength<T>(item: T): number {
+  return item.length
+}
+
+// ✅ FIX: Add constraint
+function getLength<T extends { length: number }>(item: T): number {
+  return item.length
+}
+
+// ✅ OR: More specific constraint
+function getLength<T extends string | any[]>(item: T): number {
+  return item.length
+}
+```
+
+**Pattern 7: React Hook Errors**
+```typescript
+// ❌ ERROR: React Hook "useState" cannot be called in a function
+function MyComponent() {
+  if (condition) {
+    const [state, setState] = useState(0) // ERROR!
+  }
+}
+
+// ✅ FIX: Move hooks to top level
+function MyComponent() {
+  const [state, setState] = useState(0)
+
+  if (!condition) {
+    return null
+  }
+
+  // Use state here
+}
+```
+
+**Pattern 8: Async/Await Errors**
+```typescript
+// ❌ ERROR: 'await' expressions are only allowed within async functions
+function fetchData() {
+  const data = await fetch('/api/data')
+}
+
+// ✅ FIX: Add async keyword
+async function fetchData() {
+  const data = await fetch('/api/data')
+}
+```
+
+**Pattern 9: Module Not Found**
+```typescript
+// ❌ ERROR: Cannot find module 'react' or its corresponding type declarations
+import React from 'react'
+
+// ✅ FIX: Install dependencies
+pnpm add react
+pnpm add -D @types/react
+
+// ✅ CHECK: Verify package.json has dependency
+{
+  "dependencies": {
+    "react": "^18.0.0"
+  },
+  "devDependencies": {
+    "@types/react": "^18.0.0"
+  }
+}
+```
+
+## Minimal Diff Strategy
+
+**CRITICAL: Make smallest possible changes**
+
+### DO:
+✅ Add type annotations where missing
+✅ Add null checks where needed
+✅ Fix imports/exports
+✅ Add missing dependencies
+✅ Update type definitions
+✅ Fix configuration files
+
+### DON'T:
+❌ Refactor unrelated code
+❌ Change architecture
+❌ Rename variables/functions (unless causing error)
+❌ Add new features
+❌ Change logic flow (unless fixing error)
+❌ Optimize performance
+❌ Improve code style
+
+**Example of Minimal Diff:**
+
+```typescript
+// File has 200 lines, error on line 45
+
+// ❌ WRONG: Refactor entire file
+// - Rename variables
+// - Extract functions
+// - Change patterns
+// Result: 50 lines changed
+
+// ✅ CORRECT: Fix only the error
+// - Add type annotation on line 45
+// Result: 1 line changed
+
+function processData(data) { // Line 45 - ERROR: 'data' implicitly has 'any' type
+  return data.map(item => item.value)
+}
+
+// ✅ MINIMAL FIX:
+function processData(data: any[]) { // Only change this line
+  return data.map(item => item.value)
+}
+
+// ✅ BETTER MINIMAL FIX (if type known):
+function processData(data: Array<{ value: number }>) {
+  return data.map(item => item.value)
+}
+```
+
+## Build Error Report Format
+
+```markdown
+# Build Error Resolution Report
+
+**Date:** YYYY-MM-DD
+**Build Target:** Next.js Production / TypeScript Check / Biome
+**Initial Errors:** X
+**Errors Fixed:** Y
+**Build Status:** ✅ PASSING / ❌ FAILING
+
+## Errors Fixed
+
+### 1. [Error Category - e.g., Type Inference]
+**Location:** `apps/app/src/components/PageCard.tsx:45`
+**Error Message:**
+```
+Parameter 'page' implicitly has an 'any' type.
+```
+
+**Root Cause:** Missing type annotation for function parameter
+
+**Fix Applied:**
+```diff
+- function getPagePath(page) {
++ function getPagePath(page: IPage) {
+    return page.path;
+  }
+```
+
+**Lines Changed:** 1
+**Impact:** NONE - Type safety improvement only
+
+---
+
+## Verification Steps
+
+1. ✅ TypeScript check passes: `turbo run lint --filter {package}`
+2. ✅ Next.js build succeeds: `turbo run build --filter {package}`
+3. ✅ No new errors introduced
+4. ✅ Development server runs: `turbo run dev`
+
+## Summary
+
+- Total errors resolved: X
+- Total lines changed: Y
+- Build status: ✅ PASSING
+- Blocking issues: 0 remaining
+```
+
+## When to Use This Agent
+
+**USE when:**
+- `turbo run build --filter {package}` fails
+- `turbo run lint --filter {package}` shows errors
+- Type errors blocking development
+- Import/module resolution errors
+- Configuration errors
+- Dependency version conflicts
+
+**DON'T USE when:**
+- Code needs refactoring
+- Architectural changes needed
+- New features required
+- Tests failing (run tests separately)
+- Security issues found (use security-reviewer)
+
+## Build Error Priority Levels
+
+### 🔴 CRITICAL (Fix Immediately)
+- Build completely broken
+- No development server
+- Production deployment blocked
+- Multiple files failing
+
+### 🟡 HIGH (Fix Soon)
+- Single file failing
+- Type errors in new code
+- Import errors
+- Non-critical build warnings
+
+### 🟢 MEDIUM (Fix When Possible)
+- Biome warnings
+- Deprecated API usage
+- Non-strict type issues
+- Minor configuration warnings
+
+## Success Metrics
+
+After build error resolution:
+- ✅ `turbo run lint --filter {package}` exits with code 0
+- ✅ `turbo run build --filter {package}` completes successfully
+- ✅ No new errors introduced
+- ✅ Minimal lines changed (< 5% of affected file)
+- ✅ Build time not significantly increased
+- ✅ Development server runs without errors
+- ✅ Tests still passing
+
+---
+
+**Remember**: The goal is to fix errors quickly with minimal changes. Don't refactor, don't optimize, don't redesign. Fix the error, verify the build passes, move on. Speed and precision over perfection.

+ 144 - 0
.claude/agents/security-reviewer.md

@@ -0,0 +1,144 @@
+---
+name: security-reviewer
+description: Security vulnerability detection specialist for GROWI. Use after writing code that handles user input, authentication, API endpoints, or sensitive data. Flags secrets, injection, XSS, and OWASP Top 10 vulnerabilities.
+tools: Read, Write, Edit, Bash, Grep, Glob
+model: opus
+---
+
+# Security Reviewer
+
+You are a security specialist focused on identifying vulnerabilities in the GROWI codebase. Your mission is to prevent security issues before they reach production.
+
+## GROWI Security Stack
+
+GROWI uses these security measures:
+- **helmet**: Security headers
+- **express-mongo-sanitize**: NoSQL injection prevention
+- **xss**, **rehype-sanitize**: XSS prevention
+- **Passport.js**: Authentication (Local, LDAP, SAML, OAuth)
+
+## Security Review Workflow
+
+### 1. Automated Checks
+```bash
+# Check for vulnerable dependencies
+pnpm audit
+
+# Search for potential secrets
+grep -r "api[_-]?key\|password\|secret\|token" --include="*.ts" --include="*.tsx" .
+```
+
+### 2. OWASP Top 10 Checklist
+
+1. **Injection (NoSQL)** - Are Mongoose queries safe? No string concatenation in queries?
+2. **Broken Authentication** - Passwords hashed? Sessions secure? Passport configured correctly?
+3. **Sensitive Data Exposure** - Secrets in env vars? HTTPS enforced? Logs sanitized?
+4. **Broken Access Control** - Authorization on all routes? CORS configured?
+5. **Security Misconfiguration** - Helmet enabled? Debug mode off in production?
+6. **XSS** - Output escaped? Content-Security-Policy set?
+7. **Components with Vulnerabilities** - `pnpm audit` clean?
+8. **Insufficient Logging** - Security events logged?
+
+## Vulnerability Patterns
+
+### Hardcoded Secrets (CRITICAL)
+```typescript
+// ❌ CRITICAL
+const apiKey = "sk-xxxxx"
+
+// ✅ CORRECT
+const apiKey = process.env.API_KEY
+```
+
+### NoSQL Injection (CRITICAL)
+```typescript
+// ❌ CRITICAL: Unsafe query
+const user = await User.findOne({ email: req.body.email, password: req.body.password })
+
+// ✅ CORRECT: Use express-mongo-sanitize middleware + validate input
+```
+
+### XSS (HIGH)
+```typescript
+// ❌ HIGH: Direct HTML insertion
+element.innerHTML = userInput
+
+// ✅ CORRECT: Use textContent or sanitize
+element.textContent = userInput
+// OR use xss library
+import xss from 'xss'
+element.innerHTML = xss(userInput)
+```
+
+### SSRF (HIGH)
+```typescript
+// ❌ HIGH: User-controlled URL
+const response = await fetch(userProvidedUrl)
+
+// ✅ CORRECT: Validate URL against allowlist
+const allowedDomains = ['api.example.com']
+const url = new URL(userProvidedUrl)
+if (!allowedDomains.includes(url.hostname)) {
+  throw new Error('Invalid URL')
+}
+```
+
+### Authorization Check (CRITICAL)
+```typescript
+// ❌ CRITICAL: No authorization
+app.get('/api/page/:id', async (req, res) => {
+  const page = await Page.findById(req.params.id)
+  res.json(page)
+})
+
+// ✅ CORRECT: Check user access
+app.get('/api/page/:id', loginRequired, async (req, res) => {
+  const page = await Page.findById(req.params.id)
+  if (!page.isAccessibleBy(req.user)) {
+    return res.status(403).json({ error: 'Forbidden' })
+  }
+  res.json(page)
+})
+```
+
+## Security Report Format
+
+```markdown
+## Security Review Summary
+- **Critical Issues:** X
+- **High Issues:** Y
+- **Risk Level:** 🔴 HIGH / 🟡 MEDIUM / 🟢 LOW
+
+### Issues Found
+1. **[SEVERITY]** Description @ `file:line`
+   - Impact: ...
+   - Fix: ...
+```
+
+## When to Review
+
+**ALWAYS review when:**
+- New API endpoints added
+- Authentication/authorization changed
+- User input handling added
+- Database queries modified
+- File upload features added
+- Dependencies updated
+
+## Best Practices
+
+1. **Defense in Depth** - Multiple security layers
+2. **Least Privilege** - Minimum permissions
+3. **Fail Securely** - Errors don't expose data
+4. **Separation of Concerns** - Isolate security-critical code
+5. **Keep it Simple** - Complex code has more vulnerabilities
+6. **Don't Trust Input** - Validate everything
+7. **Update Regularly** - Keep dependencies current
+
+## Emergency Response
+
+If CRITICAL vulnerability found:
+1. Document the issue
+2. Provide secure code fix
+3. Check if vulnerability was exploited
+4. Rotate any exposed secrets

+ 80 - 0
.claude/commands/learn.md

@@ -0,0 +1,80 @@
+---
+name: learn
+description: /learn - Pattern Extraction for GROWI
+---
+
+# /learn - Pattern Extraction for GROWI
+
+Extract reusable problem-solving patterns from development sessions and save them as auto-invoked Skills.
+
+## Core Purpose
+
+Capture "non-trivial problems" solved during GROWI development, converting them into reusable skills that will be automatically applied in future sessions.
+
+## Pattern Categories to Extract
+
+Focus on four key areas:
+
+1. **Error Resolution** — Document what went wrong, root causes, and fixes applicable to similar issues (e.g., Mongoose query pitfalls, Next.js hydration errors, TypeScript strict mode issues)
+
+2. **Debugging Techniques** — Capture non-obvious diagnostic steps and tool combinations (e.g., MongoDB query profiling, React DevTools with Jotai, Vitest debugging patterns)
+
+3. **Workarounds** — Record library quirks, API limitations, and version-specific solutions (e.g., @headless-tree edge cases, Socket.io reconnection handling, SWR cache invalidation)
+
+4. **GROWI Patterns** — Note codebase conventions, architecture decisions, and integration approaches (e.g., feature-based structure, Jotai + Socket.io sync, API v3 design patterns)
+
+## Skill File Structure
+
+Extracted patterns are saved in `.claude/skills/learned/{topic-name}/SKILL.md` with:
+
+```yaml
+---
+name: descriptive-name
+description: Brief description (auto-invoked when working on related code)
+---
+
+## Problem
+[What was the issue]
+
+## Solution
+[How it was solved]
+
+## Example
+[Code snippet or scenario]
+
+## When to Apply
+[Specific conditions where this pattern is useful]
+```
+
+## GROWI-Specific Examples
+
+Topics commonly learned in GROWI development:
+- `virtualized-tree-patterns` — @headless-tree + @tanstack/react-virtual optimizations
+- `socket-jotai-integration` — Real-time state synchronization patterns
+- `api-v3-error-handling` — RESTful API error response patterns
+- `jotai-atom-composition` — Derived atoms and state composition
+- `mongodb-query-optimization` — Mongoose indexing and aggregation patterns
+
+## Quality Guidelines
+
+**Extract:**
+- Patterns that will save time in future sessions
+- Non-obvious solutions worth remembering
+- Integration techniques between GROWI's tech stack
+- Performance optimizations with measurable impact
+
+**Avoid:**
+- Trivial fixes (typos, syntax errors)
+- One-time issues (service outages, environment-specific problems)
+- Information already documented in existing Skills
+- Feature-specific details (these stay in code comments)
+
+## Workflow
+
+1. User triggers `/learn` after solving a complex problem
+2. Review the session to identify valuable patterns
+3. Draft skill file(s) with clear structure
+4. Save to `.claude/skills/learned/{topic-name}/SKILL.md`
+5. Skills automatically apply in future sessions when working on related code
+
+Learned skills are automatically invoked based on their description when working on related code.

+ 186 - 0
.claude/commands/tdd.md

@@ -0,0 +1,186 @@
+---
+name: tdd
+description: Enforce test-driven development workflow. Scaffold interfaces, generate tests FIRST, then implement minimal code to pass. Ensure 80%+ coverage.
+---
+
+# TDD Command
+
+This command enforces test-driven development methodology for GROWI.
+
+## What This Command Does
+
+1. **Scaffold Interfaces** - Define types/interfaces first
+2. **Generate Tests First** - Write failing tests (RED)
+3. **Implement Minimal Code** - Write just enough to pass (GREEN)
+4. **Refactor** - Improve code while keeping tests green (REFACTOR)
+5. **Verify Coverage** - Ensure 80%+ test coverage
+
+## When to Use
+
+Use `/tdd` when:
+- Implementing new features
+- Adding new functions/components
+- Fixing bugs (write test that reproduces bug first)
+- Refactoring existing code
+- Building critical business logic
+
+## TDD Cycle
+
+```
+RED → GREEN → REFACTOR → REPEAT
+
+RED:      Write a failing test
+GREEN:    Write minimal code to pass
+REFACTOR: Improve code, keep tests passing
+REPEAT:   Next feature/scenario
+```
+
+## Example Usage
+
+```
+User: /tdd I need a function to validate page paths
+
+Agent:
+```
+
+# TDD Session: Page Path Validator
+
+## Step 1: Define Interface (SCAFFOLD)
+
+```typescript
+// src/utils/page-path-validator.ts
+export interface ValidationResult {
+  isValid: boolean;
+  errorMessage?: string;
+}
+
+export function validatePagePath(path: string): ValidationResult {
+  throw new Error('Not implemented');
+}
+```
+
+## Step 2: Write Failing Test (RED)
+
+```typescript
+// src/utils/page-path-validator.spec.ts
+describe('validatePagePath', () => {
+  it('should return valid for correct path', () => {
+    const result = validatePagePath('/user/docs/page');
+    expect(result.isValid).toBe(true);
+  });
+
+  it('should reject path without leading slash', () => {
+    const result = validatePagePath('user/docs');
+    expect(result.isValid).toBe(false);
+    expect(result.errorMessage).toBeDefined();
+  });
+
+  it('should reject empty path', () => {
+    const result = validatePagePath('');
+    expect(result.isValid).toBe(false);
+  });
+});
+```
+
+## Step 3: Run Tests - Verify FAIL
+
+```bash
+turbo run test --filter @growi/app -- src/utils/page-path-validator.spec.ts
+
+FAIL src/utils/page-path-validator.spec.ts
+  ✕ should return valid for correct path
+    Error: Not implemented
+```
+
+✅ Tests fail as expected. Ready to implement.
+
+## Step 4: Implement Minimal Code (GREEN)
+
+```typescript
+export function validatePagePath(path: string): ValidationResult {
+  if (!path) {
+    return { isValid: false, errorMessage: 'Path cannot be empty' };
+  }
+  if (!path.startsWith('/')) {
+    return { isValid: false, errorMessage: 'Path must start with /' };
+  }
+  return { isValid: true };
+}
+```
+
+## Step 5: Run Tests - Verify PASS
+
+```bash
+turbo run test --filter @growi/app -- src/utils/page-path-validator.spec.ts
+
+PASS  ✓ All tests passing!
+```
+
+## Step 6: Check Coverage
+
+```bash
+pnpm --filter @growi/app vitest run --coverage src/utils/page-path-validator.spec.ts
+
+Coverage: 100% ✅ (Target: 80%)
+```
+
+## TDD Best Practices
+
+**DO:**
+- ✅ Write the test FIRST, before any implementation
+- ✅ Run tests and verify they FAIL before implementing
+- ✅ Write minimal code to make tests pass
+- ✅ Refactor only after tests are green
+- ✅ Add edge cases and error scenarios
+- ✅ Aim for 80%+ coverage (100% for critical code)
+- ✅ Use `vitest-mock-extended` for type-safe mocks
+
+**DON'T:**
+- ❌ Write implementation before tests
+- ❌ Skip running tests after each change
+- ❌ Write too much code at once
+- ❌ Ignore failing tests
+- ❌ Test implementation details (test behavior)
+- ❌ Mock everything (prefer integration tests)
+
+## Test Types to Include
+
+**Unit Tests** (`*.spec.ts`):
+- Happy path scenarios
+- Edge cases (empty, null, max values)
+- Error conditions
+- Boundary values
+
+**Integration Tests** (`*.integ.ts`):
+- API endpoints
+- Database operations
+- External service calls
+
+**Component Tests** (`*.spec.tsx`):
+- React components with hooks
+- User interactions
+- Jotai state integration
+
+## Coverage Requirements
+
+- **80% minimum** for all code
+- **100% required** for:
+  - Authentication/authorization logic
+  - Security-critical code
+  - Core business logic (page operations, permissions)
+  - Data validation utilities
+
+## Important Notes
+
+**MANDATORY**: Tests must be written BEFORE implementation. The TDD cycle is:
+
+1. **RED** - Write failing test
+2. **GREEN** - Implement to pass
+3. **REFACTOR** - Improve code
+
+Never skip the RED phase. Never write code before tests.
+
+## Related Skills
+
+This command uses patterns from:
+- **growi-testing-patterns** - Vitest, React Testing Library, vitest-mock-extended

+ 217 - 0
.claude/rules/coding-style.md

@@ -0,0 +1,217 @@
+# Coding Style
+
+General coding standards and best practices. These rules apply to all code in the GROWI monorepo.
+
+## Immutability (CRITICAL)
+
+ALWAYS create new objects, NEVER mutate:
+
+```typescript
+// ❌ WRONG: Mutation
+function updateUser(user, name) {
+  user.name = name  // MUTATION!
+  return user
+}
+
+// ✅ CORRECT: Immutability
+function updateUser(user, name) {
+  return {
+    ...user,
+    name
+  }
+}
+
+// ✅ CORRECT: Array immutable update
+const updatedPages = pages.map(p => p.id === id ? { ...p, title: newTitle } : p);
+
+// ❌ WRONG: Array mutation
+pages[index].title = newTitle;
+```
+
+## File Organization
+
+MANY SMALL FILES > FEW LARGE FILES:
+
+- High cohesion, low coupling
+- 200-400 lines typical, 800 max
+- Functions < 50 lines
+- Extract utilities from large components
+- Organize by feature/domain, not by type
+
+## Naming Conventions
+
+### Variables and Functions
+
+- **camelCase** for variables and functions
+- **PascalCase** for classes, interfaces, types, React components
+- **UPPER_SNAKE_CASE** for constants
+
+```typescript
+const pageId = '123';
+const MAX_PAGE_SIZE = 1000;
+
+function getPageById(id: string) { }
+class PageService { }
+interface PageData { }
+type PageStatus = 'draft' | 'published';
+```
+
+### Files and Directories
+
+- **PascalCase** for React components: `Button.tsx`, `PageTree.tsx`
+- **kebab-case** for utilities: `page-utils.ts`
+- **lowercase** for directories: `features/page-tree/`, `utils/`
+
+## Export Style
+
+**Prefer named exports** over default exports:
+
+```typescript
+// ✅ Good: Named exports
+export const MyComponent = () => { };
+export function myFunction() { }
+export class MyClass { }
+
+// ❌ Avoid: Default exports
+export default MyComponent;
+```
+
+**Why?**
+- Better refactoring (IDEs can reliably rename across files)
+- Better tree shaking
+- Explicit imports improve readability
+- No ambiguity (import name matches export name)
+
+**Exception**: Next.js pages require default exports.
+
+## Type Safety
+
+**Always provide explicit types** for function parameters and return values:
+
+```typescript
+// ✅ Good: Explicit types
+function createPage(path: string, body: string): Promise<Page> {
+  // ...
+}
+
+// ❌ Avoid: Implicit any
+function createPage(path, body) {
+  // ...
+}
+```
+
+Use `import type` for type-only imports:
+
+```typescript
+import type { PageData } from '~/interfaces/page';
+```
+
+## Error Handling
+
+ALWAYS handle errors comprehensively:
+
+```typescript
+try {
+  const result = await riskyOperation();
+  return result;
+} catch (error) {
+  logger.error('Operation failed:', { error, context });
+  throw new Error('Detailed user-friendly message');
+}
+```
+
+## Async/Await
+
+Prefer async/await over Promise chains:
+
+```typescript
+// ✅ Good: async/await
+async function loadPages() {
+  const pages = await fetchPages();
+  const enriched = await enrichPageData(pages);
+  return enriched;
+}
+
+// ❌ Avoid: Promise chains
+function loadPages() {
+  return fetchPages()
+    .then(pages => enrichPageData(pages))
+    .then(enriched => enriched);
+}
+```
+
+## Comments
+
+**Write comments in English** (even for Japanese developers):
+
+```typescript
+// ✅ Good: English comment
+// Calculate the total number of pages in the workspace
+
+// ❌ Avoid: Japanese comment
+// ワークスペース内のページ総数を計算
+```
+
+**When to comment**:
+- Complex algorithms or business logic
+- Non-obvious workarounds
+- Public APIs and interfaces
+
+**When NOT to comment**:
+- Self-explanatory code (good naming is better)
+- Restating what the code does
+
+## Test File Placement
+
+**Co-locate tests with source files** in the same directory:
+
+```
+src/utils/
+├── helper.ts
+└── helper.spec.ts        # Test next to source
+
+src/components/Button/
+├── Button.tsx
+└── Button.spec.tsx       # Test next to component
+```
+
+### Test File Naming
+
+- Unit tests: `*.spec.{ts,js}`
+- Integration tests: `*.integ.ts`
+- Component tests: `*.spec.{tsx,jsx}`
+
+## Git Commit Messages
+
+Follow conventional commit format:
+
+```
+<type>(<scope>): <subject>
+
+<body>
+```
+
+**Types**: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`
+
+**Example**:
+```
+feat(page-tree): add virtualization for large trees
+
+Implemented react-window for virtualizing page tree
+to improve performance with 10k+ pages.
+```
+
+## Code Quality Checklist
+
+Before marking work complete:
+
+- [ ] Code is readable and well-named
+- [ ] Functions are small (<50 lines)
+- [ ] Files are focused (<800 lines)
+- [ ] No deep nesting (>4 levels)
+- [ ] Proper error handling
+- [ ] No console.log statements (use logger)
+- [ ] No mutation (immutable patterns used)
+- [ ] Named exports (except Next.js pages)
+- [ ] English comments
+- [ ] Co-located tests

+ 37 - 0
.claude/rules/performance.md

@@ -0,0 +1,37 @@
+# Performance Optimization
+
+## Model Selection Strategy
+
+**Haiku** - Lightweight tasks:
+- Frequent, simple agent invocations
+- Straightforward code generation
+- Worker agents in multi-agent systems
+
+**Sonnet** - Standard development:
+- Main development work
+- Orchestrating multi-agent workflows
+- Most coding tasks
+
+**Opus** - Complex reasoning:
+- Architectural decisions
+- Difficult debugging
+- Research and analysis
+
+## Context Window Management
+
+Avoid last 20% of context for:
+- Large-scale refactoring
+- Multi-file feature implementation
+- Complex debugging sessions
+
+Lower context sensitivity:
+- Single-file edits
+- Simple bug fixes
+- Documentation updates
+
+## Build Troubleshooting
+
+If build fails:
+1. Use **build-error-resolver** agent
+2. Run `turbo run lint --filter {package}`
+3. Fix incrementally, verify after each fix

+ 33 - 0
.claude/rules/security.md

@@ -0,0 +1,33 @@
+# Security Guidelines
+
+## Mandatory Security Checks
+
+Before ANY commit:
+- [ ] No hardcoded secrets (API keys, passwords, tokens)
+- [ ] All user inputs validated and sanitized
+- [ ] NoSQL injection prevention (use Mongoose properly)
+- [ ] XSS prevention (sanitize HTML output)
+- [ ] CSRF protection enabled
+- [ ] Authentication/authorization verified
+- [ ] Error messages don't leak sensitive data
+
+## Secret Management
+
+```typescript
+// NEVER: Hardcoded secrets
+const apiKey = "sk-xxxxx"
+
+// ALWAYS: Environment variables
+const apiKey = process.env.API_KEY
+if (!apiKey) {
+  throw new Error('API_KEY not configured')
+}
+```
+
+## Security Response Protocol
+
+If security issue found:
+1. STOP immediately
+2. Use **security-reviewer** agent
+3. Fix CRITICAL issues before continuing
+4. Rotate any exposed secrets

+ 206 - 0
.claude/skills/growi-monorepo-overview/SKILL.md

@@ -0,0 +1,206 @@
+---
+name: growi-monorepo-overview
+description: GROWI monorepo structure, workspace organization, and architectural principles. Auto-invoked for all GROWI development work.
+user-invocable: false
+---
+
+# GROWI Monorepo Overview
+
+GROWI is a team collaboration wiki platform built as a monorepo using **pnpm workspace + Turborepo**.
+
+## Monorepo Structure
+
+```
+growi/
+├── apps/                    # Applications
+│   ├── app/                # Main GROWI application (Next.js + Express + MongoDB)
+│   ├── pdf-converter/      # PDF conversion microservice (Ts.ED + Puppeteer)
+│   └── slackbot-proxy/     # Slack integration proxy (Ts.ED + TypeORM + MySQL)
+├── packages/               # Shared libraries
+│   ├── core/              # Core utilities and shared logic
+│   ├── core-styles/       # Common styles (SCSS)
+│   ├── editor/            # Markdown editor components
+│   ├── ui/                # UI component library
+│   ├── pluginkit/         # Plugin framework
+│   ├── slack/             # Slack integration utilities
+│   ├── presentation/      # Presentation mode
+│   ├── pdf-converter-client/ # PDF converter client library
+│   └── remark-*/          # Markdown plugins (remark-lsx, remark-drawio, etc.)
+└── Configuration files
+    ├── pnpm-workspace.yaml
+    ├── turbo.json
+    ├── package.json
+    └── .changeset/
+```
+
+## Workspace Management
+
+### pnpm Workspace
+
+All packages are managed via **pnpm workspace**. Package references use the `workspace:` protocol:
+
+```json
+{
+  "dependencies": {
+    "@growi/core": "workspace:^",
+    "@growi/ui": "workspace:^"
+  }
+}
+```
+
+### Turborepo Orchestration
+
+Turborepo handles task orchestration with caching and parallelization:
+
+```bash
+# Run tasks across all workspaces
+turbo run dev
+turbo run test
+turbo run lint
+turbo run build
+
+# Filter to specific package
+turbo run test --filter @growi/app
+turbo run lint --filter @growi/core
+```
+
+## Architectural Principles
+
+### 1. Feature-Based Architecture (Recommended)
+
+**All packages should prefer feature-based organization**:
+
+```
+{package}/src/
+├── features/              # Feature modules
+│   ├── {feature-name}/
+│   │   ├── index.ts      # Main export
+│   │   ├── interfaces/   # TypeScript types
+│   │   ├── server/       # Server-side logic (if applicable)
+│   │   ├── client/       # Client-side logic (if applicable)
+│   │   └── utils/        # Shared utilities
+```
+
+**Benefits**:
+- Clear boundaries between features
+- Easy to locate related code
+- Facilitates gradual migration from legacy structure
+
+### 2. Server-Client Separation
+
+For full-stack packages (like apps/app), separate server and client logic:
+
+- **Server code**: Node.js runtime, database access, API routes
+- **Client code**: Browser runtime, React components, UI state
+
+This enables better code splitting and prevents server-only code from being bundled into client.
+
+### 3. Shared Libraries in packages/
+
+Common code should be extracted to `packages/`:
+
+- **core**: Utilities, constants, type definitions
+- **ui**: Reusable React components
+- **editor**: Markdown editor
+- **pluginkit**: Plugin system framework
+
+## Version Management with Changeset
+
+GROWI uses **Changesets** for version management and release notes:
+
+```bash
+# Add a changeset (after making changes)
+npx changeset
+
+# Version bump (generates CHANGELOGs and updates versions)
+pnpm run version-subpackages
+
+# Publish packages to npm (for @growi/core, @growi/pluginkit)
+pnpm run release-subpackages
+```
+
+### Changeset Workflow
+
+1. Make code changes
+2. Run `npx changeset` and describe the change
+3. Commit both code and `.changeset/*.md` file
+4. On release, run `pnpm run version-subpackages`
+5. Changesets automatically updates `CHANGELOG.md` and `package.json` versions
+
+### Version Schemes
+
+- **Main app** (`apps/app`): Manual versioning with RC prereleases
+  - `pnpm run version:patch`, `pnpm run version:prerelease`
+- **Shared libraries** (`packages/core`, `packages/pluginkit`): Changeset-managed
+- **Microservices** (`apps/pdf-converter`, `apps/slackbot-proxy`): Independent versioning
+
+## Package Categories
+
+### Applications (apps/)
+
+| Package | Description | Tech Stack |
+|---------|-------------|------------|
+| **@growi/app** | Main wiki application | Next.js (Pages Router), Express, MongoDB, Jotai, SWR |
+| **@growi/pdf-converter** | PDF export service | Ts.ED, Puppeteer |
+| **@growi/slackbot-proxy** | Slack bot proxy | Ts.ED, TypeORM, MySQL |
+
+### Core Libraries (packages/)
+
+| Package | Description | Published to npm |
+|---------|-------------|------------------|
+| **@growi/core** | Core utilities | ✅ |
+| **@growi/pluginkit** | Plugin framework | ✅ |
+| **@growi/ui** | UI components | ❌ (internal) |
+| **@growi/editor** | Markdown editor | ❌ (internal) |
+| **@growi/core-styles** | Common styles | ❌ (internal) |
+
+## Development Workflow
+
+### Initial Setup
+
+```bash
+# Install dependencies for all packages
+pnpm install
+
+# Bootstrap (install + build dependencies)
+turbo run bootstrap
+```
+
+### Daily Development
+
+```bash
+# Start all dev servers (apps/app + dependencies)
+turbo run dev
+
+# Run tests for specific package
+turbo run test --filter @growi/app
+
+# Lint specific package
+turbo run lint --filter @growi/core
+```
+
+### Cross-Package Development
+
+When modifying shared libraries (packages/*), ensure dependent apps reflect changes:
+
+1. Make changes to `packages/core`
+2. Turborepo automatically detects changes and rebuilds dependents
+3. Test in `apps/app` to verify
+
+## Key Configuration Files
+
+- **pnpm-workspace.yaml**: Defines workspace packages
+- **turbo.json**: Turborepo pipeline configuration
+- **.changeset/config.json**: Changeset configuration
+- **tsconfig.base.json**: Base TypeScript config for all packages
+- **vitest.workspace.mts**: Vitest workspace config
+- **biome.json**: Biome linter/formatter config
+
+## Design Principles Summary
+
+1. **Feature Isolation**: Use feature-based architecture for new code
+2. **Server-Client Separation**: Keep server and client code separate
+3. **Shared Libraries**: Extract common code to packages/
+4. **Type-Driven Development**: Define interfaces before implementation
+5. **Progressive Enhancement**: Migrate legacy code gradually
+6. **Version Control**: Use Changesets for release management

+ 261 - 0
.claude/skills/growi-tech-stack/SKILL.md

@@ -0,0 +1,261 @@
+---
+name: growi-tech-stack
+description: GROWI technology stack, build tools, and global commands. Auto-invoked for all GROWI development work.
+user-invocable: false
+---
+
+# GROWI Tech Stack
+
+## Core Technologies
+
+- **TypeScript** ~5.0.0
+- **Node.js** ^18 || ^20
+- **MongoDB** with **Mongoose** ^6.13.6 (apps/app)
+- **MySQL** with **TypeORM** 0.2.x (apps/slackbot-proxy)
+
+## Frontend Framework
+
+- **React** 18.x
+- **Next.js** (Pages Router) - Full-stack framework for apps/app
+
+## State Management & Data Fetching (Global Standard)
+
+- **Jotai** - Atomic state management (recommended for all packages with UI state)
+  - Use for UI state, form state, modal state, etc.
+  - Lightweight, TypeScript-first, minimal boilerplate
+
+- **SWR** ^2.3.2 - Data fetching with caching
+  - Use for API data fetching with automatic revalidation
+  - Works seamlessly with RESTful APIs
+
+### Why Jotai + SWR?
+
+- **Separation of concerns**: Jotai for UI state, SWR for server state
+- **Performance**: Fine-grained reactivity (Jotai) + intelligent caching (SWR)
+- **Type safety**: Both libraries have excellent TypeScript support
+- **Simplicity**: Minimal API surface, easy to learn
+
+## Build & Development Tools
+
+### Package Management
+- **pnpm** 10.4.1 - Package manager (faster, more efficient than npm/yarn)
+
+### Monorepo Orchestration
+- **Turborepo** ^2.1.3 - Build system with caching and parallelization
+
+### Linter & Formatter
+- **Biome** ^2.2.6 - Unified linter and formatter (recommended)
+  - Replaces ESLint + Prettier
+  - Significantly faster (10-100x)
+  - Configuration: `biome.json`
+
+```bash
+# Lint and format check
+biome check <files>
+
+# Auto-fix issues
+biome check --write <files>
+```
+
+- **Stylelint** ^16.5.0 - SCSS/CSS linter
+  - Configuration: `.stylelintrc.js`
+
+```bash
+# Lint styles
+stylelint "src/**/*.scss"
+```
+
+### Testing
+- **Vitest** ^2.1.1 - Unit and integration testing (recommended)
+  - Fast, Vite-powered
+  - Jest-compatible API
+  - Configuration: `vitest.workspace.mts`
+
+- **React Testing Library** ^16.0.1 - Component testing
+  - User-centric testing approach
+
+- **vitest-mock-extended** ^2.0.2 - Type-safe mocking
+  - TypeScript autocomplete for mocks
+
+- **Playwright** ^1.49.1 - E2E testing
+  - Cross-browser testing
+
+## Essential Commands (Global)
+
+### Development
+
+```bash
+# Start all dev servers (apps/app + dependencies)
+turbo run dev
+
+# Start dev server for specific package
+turbo run dev --filter @growi/app
+
+# Install dependencies for all packages
+pnpm install
+
+# Bootstrap (install + build dependencies)
+turbo run bootstrap
+```
+
+### Testing & Quality
+
+```bash
+# Run tests for specific package
+turbo run test --filter @growi/app
+
+# Run linters for specific package
+turbo run lint --filter @growi/app
+```
+
+### Building
+
+```bash
+# Build all packages
+turbo run build
+
+# Build specific package
+turbo run build --filter @growi/core
+```
+
+## Turborepo Task Filtering
+
+Turborepo uses `--filter` to target specific packages:
+
+```bash
+# Run task for single package
+turbo run test --filter @growi/app
+
+# Run task for multiple packages
+turbo run build --filter @growi/core --filter @growi/ui
+
+# Run task for package and its dependencies
+turbo run build --filter @growi/app...
+```
+
+## Important Configuration Files
+
+### Workspace Configuration
+- **pnpm-workspace.yaml** - Defines workspace packages
+  ```yaml
+  packages:
+    - 'apps/*'
+    - 'packages/*'
+  ```
+
+### Build Configuration
+- **turbo.json** - Turborepo pipeline configuration
+  - Defines task dependencies, caching, and outputs
+
+### TypeScript Configuration
+- **tsconfig.base.json** - Base TypeScript config extended by all packages
+  - **Target**: ESNext
+  - **Module**: ESNext
+  - **Strict Mode**: Enabled (`strict: true`)
+  - **Module Resolution**: Bundler
+  - **Allow JS**: true (for gradual migration)
+  - **Isolated Modules**: true (required for Vite, SWC)
+
+Package-specific tsconfig.json example:
+```json
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+    "outDir": "./dist",
+    "rootDir": "./src"
+  },
+  "include": ["src/**/*"],
+  "exclude": ["node_modules", "dist", "**/*.spec.ts"]
+}
+```
+
+### Testing Configuration
+- **vitest.workspace.mts** - Vitest workspace config
+  - Defines test environments (Node.js, happy-dom)
+  - Configures coverage
+
+### Linter Configuration
+- **biome.json** - Biome linter/formatter config
+  - Rules, ignore patterns, formatting options
+
+## Development Best Practices
+
+### Command Usage
+
+1. **Always use Turborepo for cross-package tasks**:
+   - ✅ `turbo run test --filter @growi/app`
+   - ❌ `cd apps/app && pnpm test` (bypasses Turborepo caching)
+
+2. **Use pnpm for package management**:
+   - ✅ `pnpm install`
+   - ❌ `npm install` or `yarn install`
+
+3. **Run tasks from workspace root**:
+   - Turborepo handles cross-package dependencies
+   - Caching works best from root
+
+### State Management Guidelines
+
+1. **Use Jotai for UI state**:
+   ```typescript
+   // Example: Modal state
+   import { atom } from 'jotai';
+
+   export const isModalOpenAtom = atom(false);
+   ```
+
+2. **Use SWR for server state**:
+   ```typescript
+   // Example: Fetching pages
+   import useSWR from 'swr';
+
+   const { data, error, isLoading } = useSWR('/api/pages', fetcher);
+   ```
+
+3. **Avoid mixing concerns**:
+   - Don't store server data in Jotai atoms
+   - Don't manage UI state with SWR
+
+## Migration Notes
+
+- **New packages**: Use Biome + Vitest from the start
+- **Legacy packages**: Can continue using existing tools during migration
+- **Gradual migration**: Prefer updating to Biome + Vitest when modifying existing files
+
+## Technology Decisions
+
+### Why Next.js Pages Router (not App Router)?
+
+- GROWI started before App Router was stable
+- Pages Router is well-established and stable
+- Migration to App Router is being considered for future versions
+
+### Why Jotai (not Redux/Zustand)?
+
+- **Atomic approach**: More flexible than Redux, simpler than Recoil
+- **TypeScript-first**: Excellent type inference
+- **Performance**: Fine-grained reactivity, no unnecessary re-renders
+- **Minimal boilerplate**: Less code than Redux
+
+### Why SWR (not React Query)?
+
+- **Simplicity**: Smaller API surface
+- **Vercel integration**: Built by Vercel (same as Next.js)
+- **Performance**: Optimized for Next.js SSR/SSG
+
+### Why Biome (not ESLint + Prettier)?
+
+- **Speed**: 10-100x faster than ESLint
+- **Single tool**: Replaces both ESLint and Prettier
+- **Consistency**: No conflicts between linter and formatter
+- **Growing ecosystem**: Active development, Rust-based
+
+## Package-Specific Tech Stacks
+
+Different apps in the monorepo may use different tech stacks:
+
+- **apps/app**: Next.js + Express + MongoDB + Jotai + SWR
+- **apps/pdf-converter**: Ts.ED + Puppeteer
+- **apps/slackbot-proxy**: Ts.ED + TypeORM + MySQL
+
+See package-specific CLAUDE.md or skills for details.

+ 436 - 0
.claude/skills/growi-testing-patterns/SKILL.md

@@ -0,0 +1,436 @@
+---
+name: growi-testing-patterns
+description: GROWI testing patterns with Vitest, React Testing Library, and vitest-mock-extended. Auto-invoked for all GROWI development work.
+user-invocable: false
+---
+
+# GROWI Testing Patterns
+
+GROWI uses **Vitest** for all testing (unit, integration, component). This skill covers universal testing patterns applicable across the monorepo.
+
+## Test File Placement (Global Standard)
+
+Place test files **in the same directory** as the source file:
+
+```
+src/components/Button/
+├── Button.tsx
+└── Button.spec.tsx       # Component test
+
+src/utils/
+├── helper.ts
+└── helper.spec.ts        # Unit test
+
+src/services/api/
+├── pageService.ts
+└── pageService.integ.ts  # Integration test
+```
+
+## Test Types & Environments
+
+| File Pattern | Type | Environment | Use Case |
+|--------------|------|-------------|----------|
+| `*.spec.{ts,js}` | Unit Test | Node.js | Pure functions, utilities, services |
+| `*.integ.ts` | Integration Test | Node.js + DB | API routes, database operations |
+| `*.spec.{tsx,jsx}` | Component Test | happy-dom | React components |
+
+Vitest automatically selects the environment based on file extension and configuration.
+
+## Vitest Configuration
+
+### Global APIs (No Imports Needed)
+
+All GROWI packages configure Vitest globals in `tsconfig.json`:
+
+```json
+{
+  "compilerOptions": {
+    "types": ["vitest/globals"]
+  }
+}
+```
+
+This enables auto-import of testing APIs:
+
+```typescript
+// No imports needed!
+describe('MyComponent', () => {
+  it('should render', () => {
+    expect(true).toBe(true);
+  });
+
+  beforeEach(() => {
+    // Setup
+  });
+
+  afterEach(() => {
+    // Cleanup
+  });
+});
+```
+
+**Available globals**: `describe`, `it`, `test`, `expect`, `beforeEach`, `afterEach`, `beforeAll`, `afterAll`, `vi`
+
+## Type-Safe Mocking with vitest-mock-extended
+
+### Basic Usage
+
+`vitest-mock-extended` provides **fully type-safe mocks** with TypeScript autocomplete:
+
+```typescript
+import { mockDeep, type DeepMockProxy } from 'vitest-mock-extended';
+
+// Create type-safe mock
+const mockRouter: DeepMockProxy<NextRouter> = mockDeep<NextRouter>();
+
+// TypeScript autocomplete works!
+mockRouter.asPath = '/test-path';
+mockRouter.query = { id: '123' };
+mockRouter.push.mockResolvedValue(true);
+
+// Use in tests
+expect(mockRouter.push).toHaveBeenCalledWith('/new-path');
+```
+
+### Complex Types with Optional Properties
+
+```typescript
+interface ComplexProps {
+  currentPageId?: string | null;
+  currentPathname?: string | null;
+  data?: Record<string, unknown>;
+  onSubmit?: (value: string) => void;
+}
+
+const mockProps: DeepMockProxy<ComplexProps> = mockDeep<ComplexProps>();
+mockProps.currentPageId = 'page-123';
+mockProps.data = { key: 'value' };
+mockProps.onSubmit?.mockImplementation((value) => {
+  console.log(value);
+});
+```
+
+### Why vitest-mock-extended?
+
+- ✅ **Type safety**: Catches typos at compile time
+- ✅ **Autocomplete**: IDE suggestions for all properties/methods
+- ✅ **Deep mocking**: Automatically mocks nested objects
+- ✅ **Vitest integration**: Works seamlessly with `vi.fn()`
+
+## React Testing Library Patterns
+
+### Basic Component Test
+
+```typescript
+import { render } from '@testing-library/react';
+import { Button } from './Button';
+
+describe('Button', () => {
+  it('should render with text', () => {
+    const { getByText } = render(<Button>Click me</Button>);
+    expect(getByText('Click me')).toBeInTheDocument();
+  });
+
+  it('should call onClick when clicked', async () => {
+    const onClick = vi.fn();
+    const { getByRole } = render(<Button onClick={onClick}>Click</Button>);
+
+    const button = getByRole('button');
+    await userEvent.click(button);
+
+    expect(onClick).toHaveBeenCalledTimes(1);
+  });
+});
+```
+
+### Testing with Jotai (Global Pattern)
+
+When testing components that use Jotai atoms, wrap with `<Provider>`:
+
+```typescript
+import { render } from '@testing-library/react';
+import { Provider } from 'jotai';
+
+const renderWithJotai = (ui: React.ReactElement) => {
+  const Wrapper = ({ children }: { children: React.ReactNode }) => (
+    <Provider>{children}</Provider>
+  );
+  return render(ui, { wrapper: Wrapper });
+};
+
+describe('ComponentWithJotai', () => {
+  it('should render with atom state', () => {
+    const { getByText } = renderWithJotai(<MyComponent />);
+    expect(getByText('Hello')).toBeInTheDocument();
+  });
+});
+```
+
+### Isolated Jotai Scope (For Testing)
+
+To isolate atom state between tests:
+
+```typescript
+import { createScope } from 'jotai-scope';
+
+describe('ComponentWithIsolatedState', () => {
+  it('test 1', () => {
+    const scope = createScope();
+    const { getByText } = renderWithJotai(<MyComponent />, scope);
+    // ...
+  });
+
+  it('test 2', () => {
+    const scope = createScope(); // Fresh scope
+    const { getByText } = renderWithJotai(<MyComponent />, scope);
+    // ...
+  });
+});
+```
+
+## Async Testing Patterns (Global Standard)
+
+### Using `act()` and `waitFor()`
+
+When testing async state updates:
+
+```typescript
+import { waitFor, act } from '@testing-library/react';
+import { renderHook } from '@testing-library/react';
+
+test('async hook', async () => {
+  const { result } = renderHook(() => useMyAsyncHook());
+
+  // Trigger async action
+  await act(async () => {
+    result.current.triggerAsyncAction();
+  });
+
+  // Wait for state update
+  await waitFor(() => {
+    expect(result.current.isLoading).toBe(false);
+  });
+
+  expect(result.current.data).toBeDefined();
+});
+```
+
+### Testing Async Functions
+
+```typescript
+it('should fetch data successfully', async () => {
+  const data = await fetchData();
+  expect(data).toEqual({ id: '123', name: 'Test' });
+});
+
+it('should handle errors', async () => {
+  await expect(fetchDataWithError()).rejects.toThrow('Error');
+});
+```
+
+## Advanced Assertions
+
+### Object Matching
+
+```typescript
+expect(mockFunction).toHaveBeenCalledWith(
+  expect.objectContaining({
+    pathname: '/expected-path',
+    data: expect.any(Object),
+    timestamp: expect.any(Number),
+  })
+);
+```
+
+### Array Matching
+
+```typescript
+expect(result).toEqual(
+  expect.arrayContaining([
+    expect.objectContaining({ id: '123' }),
+    expect.objectContaining({ id: '456' }),
+  ])
+);
+```
+
+### Partial Matching
+
+```typescript
+expect(user).toMatchObject({
+  name: 'John',
+  email: 'john@example.com',
+  // Other properties are ignored
+});
+```
+
+## Test Structure Best Practices
+
+### AAA Pattern (Arrange-Act-Assert)
+
+```typescript
+describe('MyComponent', () => {
+  beforeEach(() => {
+    vi.clearAllMocks(); // Clear mocks before each test
+  });
+
+  describe('rendering', () => {
+    it('should render with default props', () => {
+      // Arrange: Setup test data
+      const props = { title: 'Test' };
+
+      // Act: Render component
+      const { getByText } = render(<MyComponent {...props} />);
+
+      // Assert: Verify output
+      expect(getByText('Test')).toBeInTheDocument();
+    });
+  });
+
+  describe('user interactions', () => {
+    it('should submit form on button click', async () => {
+      // Arrange
+      const onSubmit = vi.fn();
+      const { getByRole, getByLabelText } = render(
+        <MyForm onSubmit={onSubmit} />
+      );
+
+      // Act
+      await userEvent.type(getByLabelText('Name'), 'John');
+      await userEvent.click(getByRole('button', { name: 'Submit' }));
+
+      // Assert
+      expect(onSubmit).toHaveBeenCalledWith({ name: 'John' });
+    });
+  });
+});
+```
+
+### Nested `describe` for Organization
+
+```typescript
+describe('PageService', () => {
+  describe('createPage', () => {
+    it('should create a page successfully', async () => {
+      // ...
+    });
+
+    it('should throw error if path is invalid', async () => {
+      // ...
+    });
+  });
+
+  describe('updatePage', () => {
+    it('should update page content', async () => {
+      // ...
+    });
+  });
+});
+```
+
+## Common Mocking Patterns
+
+### Mocking SWR
+
+```typescript
+vi.mock('swr', () => ({
+  default: vi.fn(() => ({
+    data: mockData,
+    error: null,
+    isLoading: false,
+    mutate: vi.fn(),
+  })),
+}));
+```
+
+### Mocking Modules
+
+```typescript
+// Mock entire module
+vi.mock('~/services/PageService', () => ({
+  PageService: {
+    findById: vi.fn().mockResolvedValue({ id: '123', title: 'Test' }),
+    create: vi.fn().mockResolvedValue({ id: '456', title: 'New' }),
+  },
+}));
+
+// Use in test
+import { PageService } from '~/services/PageService';
+
+it('should call PageService.findById', async () => {
+  await myFunction();
+  expect(PageService.findById).toHaveBeenCalledWith('123');
+});
+```
+
+### Mocking Specific Functions
+
+```typescript
+import { myFunction } from '~/utils/myUtils';
+
+vi.mock('~/utils/myUtils', () => ({
+  myFunction: vi.fn().mockReturnValue('mocked'),
+  otherFunction: vi.fn(), // Mock other exports
+}));
+```
+
+## Integration Tests (with Database)
+
+Integration tests (*.integ.ts) can access in-memory databases:
+
+```typescript
+describe('PageService Integration', () => {
+  beforeEach(async () => {
+    // Setup: Seed test data
+    await Page.create({ path: '/test', body: 'content' });
+  });
+
+  afterEach(async () => {
+    // Cleanup: Clear database
+    await Page.deleteMany({});
+  });
+
+  it('should create a page', async () => {
+    const page = await PageService.create({
+      path: '/new-page',
+      body: 'content',
+    });
+
+    expect(page._id).toBeDefined();
+    expect(page.path).toBe('/new-page');
+  });
+});
+```
+
+## Testing Checklist
+
+Before committing tests, ensure:
+
+- ✅ **Co-location**: Test files are next to source files
+- ✅ **Descriptive names**: Test descriptions clearly state what is being tested
+- ✅ **AAA pattern**: Tests follow Arrange-Act-Assert structure
+- ✅ **Mocks cleared**: Use `beforeEach(() => vi.clearAllMocks())`
+- ✅ **Async handled**: Use `async/await` and `waitFor()` for async operations
+- ✅ **Type safety**: Use `vitest-mock-extended` for type-safe mocks
+- ✅ **Isolated state**: Jotai tests use separate scopes if needed
+
+## Running Tests
+
+```bash
+# Run all tests for a package
+turbo run test --filter @growi/app
+
+# Run specific test file
+cd apps/app && pnpm vitest run src/components/Button/Button.spec.tsx
+```
+
+## Summary: GROWI Testing Philosophy
+
+1. **Co-locate tests**: Keep tests close to source code
+2. **Type-safe mocks**: Use `vitest-mock-extended` for TypeScript support
+3. **React Testing Library**: Test user behavior, not implementation details
+4. **Async patterns**: Use `act()` and `waitFor()` for async state updates
+5. **Jotai integration**: Wrap components with `<Provider>` for atom state
+6. **Clear structure**: Use nested `describe` and AAA pattern
+7. **Clean mocks**: Always clear mocks between tests
+
+These patterns apply to **all GROWI packages** with React/TypeScript code.

+ 116 - 49
AGENTS.md

@@ -1,73 +1,140 @@
 # AGENTS.md
 # AGENTS.md
 
 
-GROWI is a team collaboration wiki platform built with Next.js, Express, and MongoDB. This guide helps AI coding agents navigate the monorepo and work effectively with GROWI's architecture.
+GROWI is a team collaboration wiki platform built with Next.js, Express, and MongoDB. This guide provides essential instructions for AI coding agents working with the GROWI codebase.
 
 
-## Language
+## Language Policy
 
 
-If we detect at the beginning of a conversation that the user's primary language is not English, we will always respond in that language. However, we may retain technical terms in English if necessary.
+**Response Language**: If we detect at the beginning of a conversation that the user's primary language is not English, we will always respond in that language. However, technical terms may remain in English when appropriate.
 
 
-When generating source code, all comments and explanations within the code will be written in English.
+**Code Comments**: When generating source code, all comments and explanations within the code must be written in English, regardless of the conversation language.
 
 
 ## Project Overview
 ## Project Overview
 
 
-GROWI is a team collaboration software using markdown - a wiki platform with hierarchical page organization. It's built with Next.js, Express, MongoDB, and includes features like real-time collaborative editing, authentication integrations, and plugin support.
+GROWI is a team collaboration wiki platform using Markdown, featuring hierarchical page organization, real-time collaborative editing, authentication integrations, and plugin support. Built as a monorepo with Next.js, Express, and MongoDB.
 
 
-## Development Tools
-- **Package Manager**: pnpm with workspace support
-- **Build System**: Turborepo for monorepo orchestration
-- **Code Quality**: 
-  - Biome for linting and formatting
-  - Stylelint for SCSS/CSS
+## Knowledge Base
 
 
-## Development Commands
+### Claude Code Skills (Auto-Invoked)
 
 
-### Core Development
-- `turbo run bootstrap` - Install dependencies for all workspace packages
-- `turbo run dev` - Start development server (automatically runs migrations and pre-builds styles)
+Technical information is available in **Claude Code Skills** (`.claude/skills/`), which are automatically invoked during development.
 
 
-### Production Commands
-- `pnpm run app:build` - Build GROWI app client and server for production
-- `pnpm run app:server` - Launch GROWI app server in production mode
-- `pnpm start` - Build and start the application (runs both build and server commands)
+**Global Skills** (always loaded):
 
 
-### Database Migrations
-- `pnpm run migrate` - Run MongoDB migrations (production)
-- `turbo run dev:migrate @apps/app` - Run migrations in development (or wait for automatic execution with dev)
-- `cd apps/app && pnpm run dev:migrate:status` - Check migration status
-- `cd apps/app && pnpm run dev:migrate:down` - Rollback last migration
+| Skill | Description |
+|-------|-------------|
+| **growi-monorepo-overview** | Monorepo structure, workspace organization, Changeset versioning |
+| **growi-tech-stack** | Technology stack, pnpm/Turborepo, TypeScript, Biome |
+| **growi-testing-patterns** | Vitest, React Testing Library, vitest-mock-extended |
 
 
-### Testing and Quality
-- `turbo run test @apps/app` - Run Vitest test suites with coverage
-- `turbo run lint @apps/app` - Run all linters (TypeScript, Biome, Stylelint, OpenAPI)
-- `cd apps/app && pnpm run lint:typecheck` - TypeScript type checking only
-- `cd apps/app && pnpm run test:vitest` - Run Vitest unit tests
+**Rules** (always applied):
 
 
-### Development Utilities  
-- `cd apps/app && pnpm run repl` - Start Node.js REPL with application context loaded
-- `turbo run pre:styles @apps/app` - Pre-build styles with Vite
+| Rule | Description |
+|------|-------------|
+| **coding-style** | Coding conventions, naming, exports, immutability, comments |
+| **security** | Security checklist, secret management, OWASP vulnerability prevention |
+| **performance** | Model selection, context management, build troubleshooting |
 
 
-## Architecture Overview
+**Agents** (specialized):
 
 
-### Monorepo Structure
-- `/apps/app/` - Main GROWI application (Next.js frontend + Express backend)
-- `/apps/pdf-converter/` - PDF conversion microservice
-- `/apps/slackbot-proxy/` - Slack integration proxy service
-- `/packages/` - Shared libraries and components
+| Agent | Description |
+|-------|-------------|
+| **build-error-resolver** | TypeScript/build error resolution with minimal diffs |
+| **security-reviewer** | Security vulnerability detection, OWASP Top 10 |
 
 
-## File Organization Patterns
+**Commands** (user-invocable):
 
 
-### Components
-- Use TypeScript (.tsx) for React components
-- Co-locate styles as `.module.scss` files
-- Export components through `index.ts` files where appropriate
-- Group related components in feature-based directories
+| Command | Description |
+|---------|-------------|
+| **/tdd** | Test-driven development workflow |
+| **/learn** | Extract reusable patterns from sessions |
 
 
-### Tests
-- Unit Test: `*.spec.ts`
-- Integration Test: `*.integ.ts`
-- Component Test: `*.spec.tsx`
+**apps/app Skills** (loaded when working in apps/app):
 
 
+| Skill | Description |
+|-------|-------------|
+| **growi-app-architecture** | Next.js Pages Router, Express, feature-based structure |
+| **growi-app-commands** | apps/app specific commands (migrate, test, build) |
+| **growi-app-specific-patterns** | Jotai/SWR patterns, router mocking, API routes |
+
+### Package-Specific CLAUDE.md
+
+Each application has its own CLAUDE.md with detailed instructions:
+
+- `apps/app/CLAUDE.md` - Main GROWI application
+- `apps/pdf-converter/CLAUDE.md` - PDF conversion microservice
+- `apps/slackbot-proxy/CLAUDE.md` - Slack integration proxy
+
+### Serena Memories
+
+Additional detailed specifications are stored in **Serena memories** and can be referenced when needed for specific features or subsystems.
+
+## Quick Reference
+
+### Essential Commands (Global)
+
+```bash
+# Development
+turbo run dev                    # Start all dev servers
+
+# Quality Checks (use Turborepo for caching)
+turbo run lint --filter @growi/app
+turbo run test --filter @growi/app
+
+# Production
+pnpm run app:build              # Build main app
+pnpm start                      # Build and start
+```
+
+### Key Directories
+
+```
+growi/
+├── apps/
+│   ├── app/                # Main GROWI application (Next.js + Express)
+│   ├── pdf-converter/      # PDF conversion microservice
+│   └── slackbot-proxy/     # Slack integration proxy
+├── packages/               # Shared libraries (@growi/core, @growi/ui, etc.)
+└── .claude/
+    ├── skills/             # Claude Code skills (auto-loaded)
+    ├── rules/              # Coding standards (always applied)
+    ├── agents/             # Specialized agents
+    └── commands/           # User-invocable commands (/tdd, /learn)
+```
+
+## Development Guidelines
+
+1. **Feature-Based Architecture**: Create new features in `features/{feature-name}/`
+2. **Server-Client Separation**: Keep server and client code separate
+3. **State Management**: Jotai for UI state, SWR for data fetching
+4. **Named Exports**: Prefer named exports (except Next.js pages)
+5. **Test Co-location**: Place test files next to source files
+6. **Type Safety**: Use strict TypeScript throughout
+7. **Changeset**: Use `npx changeset` for version management
+
+## Before Committing
+
+Always execute these checks:
+
+```bash
+# From workspace root (recommended)
+turbo run lint:typecheck --filter @growi/app
+turbo run lint:biome --filter @growi/app
+turbo run test --filter @growi/app
+turbo run build --filter @growi/app
+```
+
+Or from apps/app directory:
+
+```bash
+pnpm run lint:typecheck
+pnpm run lint:biome
+pnpm run test:vitest
+pnpm run build
+```
 
 
 ---
 ---
 
 
-When working with this codebase, always run the appropriate linting and testing commands before committing changes. The application uses strict TypeScript checking and comprehensive test coverage requirements.
+For detailed information, refer to:
+- **Rules**: `.claude/rules/` (coding standards)
+- **Skills**: `.claude/skills/` (technical knowledge)
+- **Package docs**: `apps/*/CLAUDE.md` (package-specific)