diff --git a/.claude/plan/hooks/2602/13-claude-code-hooks-auto-setup.plan.md b/.claude/plan/hooks/2602/13-claude-code-hooks-auto-setup.plan.md deleted file mode 100644 index 2d61cf9..0000000 --- a/.claude/plan/hooks/2602/13-claude-code-hooks-auto-setup.plan.md +++ /dev/null @@ -1,146 +0,0 @@ -# Claude Code Hooks 자동 설정 시스템 - -## Business Goal -Claude Code 사용 시 작업 상태를 칸반 보드에 자동 반영하여 AI 에이전트의 작업 진행 상황을 실시간으로 모니터링할 수 있도록 한다. 프로젝트 스캔 시 hooks를 자동으로 설치하여 수동 설정 부담을 제거한다. - -## Scope -- **In Scope**: - - branchName + projectName 기반 상태 업데이트 API 엔드포인트 - - UserPromptSubmit / Stop hook 스크립트 2개 - - scanAndRegisterProjects 확장 (자동 hooks 설치) - - 기존 미사용 엔드포인트 제거 (/api/hooks/update, /api/hooks/complete) - - README.md Hook 가이드 업데이트 -- **Out of Scope**: SSH remote repo 자동 설정, UI 변경 - -## Codebase Analysis Summary -- 기존 Hook API: `/api/hooks/start`, `/api/hooks/update`, `/api/hooks/complete` — 내부에서 호출하는 코드 없음 -- Task 엔티티: `branchName` 필드 존재 (unique), `projectId` FK 존재 -- Project 엔티티: `name` 필드 (unique), `repoPath` 필드 -- 프로젝트 스캔: `scanAndRegisterProjects()` in `src/app/actions/project.ts` -- 기존 hooks 설정: `.claude/settings.json` — UserPromptSubmit, Notification, Stop 이벤트 사용 중 -- Hook 스크립트 패턴: `.claude/hooks/skill-forced-eval-hook.sh` — bash, stdin JSON 파싱, cat heredoc 출력 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/app/api/hooks/status/route.ts` | branch+project 기반 상태 업데이트 API | Create | -| `src/app/api/hooks/update/route.ts` | 기존 id 기반 업데이트 API | Delete | -| `src/app/api/hooks/complete/route.ts` | 기존 id 기반 완료 API | Delete | -| `src/lib/claudeHooksSetup.ts` | hooks 자동 설정 유틸리티 | Create | -| `src/app/actions/project.ts` | 프로젝트 스캔/등록 서버 액션 | Modify | -| `README.md` | 프로젝트 문서 | Modify | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| API route 패턴 | `/api/hooks/start/route.ts` | NextRequest/NextResponse, try-catch, JSON body 파싱 | -| 한국어 에러 메시지 | 기존 API 전체 | 에러 응답은 한국어로 | -| Entity import | 기존 코드 | `@/entities/KanbanTask`, `@/lib/database` | -| Hook 스크립트 | `skill-forced-eval-hook.sh` | bash, stdin JSON, jq 파싱 | -| Server action | `project.ts` | async function, serialize, revalidatePath | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| Task 식별 | branchName + projectName | 복수 프로젝트에서 동일 브랜치명 가능, projectName으로 구분 | branchName only | -| 미사용 API 정리 | update/complete 삭제 | status 엔드포인트로 대체, 코드 정리 | 유지 | -| hooks 설치 위치 | 각 repo의 .claude/ | 프로젝트별 독립 설정 | 글로벌 ~/.claude/ | -| 설정 병합 전략 | JSON deep merge | 기존 settings.json 보존 | 덮어쓰기 | - -## Implementation Todos - -### Todo 1: branchName + projectName 기반 상태 업데이트 API 생성 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: Hook 스크립트가 호출할 API 엔드포인트를 생성한다 -- **Work**: - - `src/app/api/hooks/status/route.ts` 생성 - - POST 핸들러: `{ branchName: string, projectName: string, status: "progress" | "review" | "done" }` 수신 - - Project를 name으로 조회 → Task를 projectId + branchName으로 조회 → status 업데이트 - - 프로젝트/태스크 미발견 시 404 응답 -- **Convention Notes**: 기존 `/api/hooks/start/route.ts`와 동일한 패턴 (try-catch, 한국어 에러) -- **Verification**: TypeScript 컴파일 확인 -- **Exit Criteria**: `curl -X POST /api/hooks/status` 호출 시 정상 응답 -- **Status**: pending - -### Todo 2: 미사용 API 엔드포인트 제거 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: `/api/hooks/update`와 `/api/hooks/complete`를 삭제하여 코드를 정리한다 -- **Work**: - - `src/app/api/hooks/update/route.ts` 삭제 - - `src/app/api/hooks/complete/route.ts` 삭제 - - `src/app/api/hooks/update/` 디렉토리 삭제 - - `src/app/api/hooks/complete/` 디렉토리 삭제 -- **Convention Notes**: 내부 코드에서 참조하는 곳 없음 확인 완료 -- **Verification**: TypeScript 컴파일 확인 -- **Exit Criteria**: 삭제된 엔드포인트 경로에 404 반환 -- **Status**: pending - -### Todo 3: Hook 스크립트 템플릿 및 자동 설정 유틸리티 생성 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 프로젝트 스캔 시 자동으로 설치할 hook 스크립트와 settings.json 병합 로직을 구현한다 -- **Work**: - - `src/lib/claudeHooksSetup.ts` 생성 - - `generatePromptHookScript(kanvibeUrl, projectName)` — UserPromptSubmit hook bash 스크립트 생성 - - `generateStopHookScript(kanvibeUrl, projectName)` — Stop hook bash 스크립트 생성 - - `setupClaudeHooks(repoPath, projectName, kanvibeUrl)` — 메인 설정 함수 - - `.claude/hooks/` 디렉토리 생성 - - 스크립트 파일 생성 + chmod +x - - 기존 `.claude/settings.json` 읽기 → hooks 섹션 deep merge → 저장 - - 병합 로직: 기존 UserPromptSubmit/Stop hooks 배열에 kanvibe hook 추가 (중복 방지) -- **Convention Notes**: - - hook 스크립트에서 `jq`로 stdin JSON 파싱, `git rev-parse --abbrev-ref HEAD`로 브랜치 획득 - - KANVIBE_URL은 스크립트 상단 변수로 주입 - - PROJECT_NAME도 스크립트 상단 변수로 주입 -- **Verification**: 유닛 레벨에서 함수 호출 시 파일 생성 확인 -- **Exit Criteria**: setupClaudeHooks 호출 시 .claude/hooks/ + settings.json 정상 생성 -- **Status**: pending - -### Todo 4: scanAndRegisterProjects에 자동 hooks 설정 통합 -- **Priority**: 2 -- **Dependencies**: Todo 3 -- **Goal**: 프로젝트 스캔/등록 후 자동으로 Claude Code hooks를 설치한다 -- **Work**: - - `src/app/actions/project.ts`의 `scanAndRegisterProjects` 수정 - - 프로젝트 등록 성공 후 `setupClaudeHooks(repoPath, projectName, kanvibeUrl)` 호출 - - `kanvibeUrl`은 `process.env.PORT`에서 추출: `http://localhost:${PORT || 4885}` - - ScanResult 타입에 `hooksSetup: string[]` 필드 추가 - - hooks 설정 실패 시 에러를 `result.errors`에 추가하되 등록은 유지 - - 로컬 repo만 대상 (sshHost가 있으면 hooks 설정 건너뜀) -- **Convention Notes**: 기존 scanAndRegisterProjects 패턴 유지, try-catch로 에러 격리 -- **Verification**: TypeScript 컴파일 확인 -- **Exit Criteria**: 프로젝트 스캔 시 등록된 repo에 .claude/hooks/ 자동 생성 -- **Status**: pending - -### Todo 5: README.md Hook 가이드 업데이트 -- **Priority**: 2 -- **Dependencies**: Todo 1, Todo 2 -- **Goal**: Claude Code Hooks 설정 방법을 README에 정확하게 문서화한다 -- **Work**: - - README.md의 "Hook API (Claude Code 연동)" 섹션 전체 재작성 - - 삭제된 update/complete 엔드포인트 예시 제거 - - 새 `/api/hooks/status` 엔드포인트 문서화 - - hooks 동작 흐름 설명 (UserPromptSubmit → PROGRESS, Stop → REVIEW) - - 수동 설정 방법 (settings.json + 스크립트 직접 작성) - - 자동 설정 방법 (프로젝트 스캔 시 자동 설치) - - 기술 스택 섹션의 Next.js 버전 16으로 업데이트 -- **Convention Notes**: 기존 README 스타일 유지, 한국어, 코드 블록 사용 -- **Verification**: 마크다운 렌더링 확인 -- **Exit Criteria**: README에 정확한 hook 설정 가이드가 포함됨 -- **Status**: pending - -## Verification Strategy -- TypeScript 컴파일: `npx tsc --noEmit` -- 삭제된 엔드포인트 참조 없음 확인 -- 생성된 hook 스크립트의 bash syntax 확인: `bash -n script.sh` - -## Progress Tracking -- Total Todos: 5 -- Completed: 5 -- Status: Execution complete - -## Change Log -- 2026-02-13: Plan created -- 2026-02-13: All 5 todos completed. TypeScript 컴파일 성공. diff --git a/.claude/plan/hooks/2602/13-cli-hooks-setup-and-ui.plan.md b/.claude/plan/hooks/2602/13-cli-hooks-setup-and-ui.plan.md deleted file mode 100644 index d9152c8..0000000 --- a/.claude/plan/hooks/2602/13-cli-hooks-setup-and-ui.plan.md +++ /dev/null @@ -1,43 +0,0 @@ -# CLI Hooks 설정 스크립트 + Task 생성 시 자동 설정 + 웹 UI - -## Business Goal -Claude Code hooks를 CLI 스크립트로 간편하게 설치하고, Task 생성 시 자동으로 worktree에 hooks를 세팅하며, 웹 UI에서 프로젝트별 hooks 상태를 확인/설치할 수 있도록 한다. - -## Scope -- **In Scope**: CLI 스크립트, createTask/branchFromTask 확장, hooks 상태 확인 함수, server action, ProjectSettings UI 확장, i18n -- **Out of Scope**: SSH remote, hooks JSON 에디터, hooks 삭제 기능 - -## Implementation Todos - -### Todo 1: CLI 스크립트 생성 -- **Priority**: 1 -- **Dependencies**: none -- **Status**: pending - -### Todo 2: createTask/branchFromTask에 hooks 자동 설정 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Status**: pending - -### Todo 3: hooks 상태 확인 함수 + server action 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Status**: pending - -### Todo 4: ProjectSettings UI 확장 -- **Priority**: 2 -- **Dependencies**: Todo 3 -- **Status**: pending - -### Todo 5: i18n 메시지 추가 -- **Priority**: 2 -- **Dependencies**: Todo 4 -- **Status**: pending - -## Progress Tracking -- Total Todos: 5 -- Completed: 0 -- Status: Planning complete - -## Change Log -- 2026-02-13: Plan created diff --git a/.claude/plan/hooks/2602/16-add-pending-status.plan.md b/.claude/plan/hooks/2602/16-add-pending-status.plan.md deleted file mode 100644 index a05f6e6..0000000 --- a/.claude/plan/hooks/2602/16-add-pending-status.plan.md +++ /dev/null @@ -1,141 +0,0 @@ -# Add PENDING Status Between Progress and Review - -## Business Goal -Claude Code hooks에서 AskUserQuestion 발생 시 사용자 의사결정 대기 상태를 별도 칸반 컬럼으로 시각화하여, progress(작업 중)와 review(검토 대기)를 명확히 구분한다. - -## Scope -- **In Scope**: TaskStatus enum에 PENDING 추가, DB migration, Hook 스크립트 수정, API STATUS_MAP 갱신, Board UI 컬럼 추가, TaskStatusBadge 스타일, i18n 번역, CSS/디자인 토큰 추가 -- **Out of Scope**: Done cleanup 로직, 기타 hook 동작 변경, Stop hook 동작 변경 - -## Codebase Analysis Summary -- `TaskStatus` enum (src/entities/KanbanTask.ts:12-17): TODO, PROGRESS, REVIEW, DONE -- Hook 스크립트 생성 (src/lib/claudeHooksSetup.ts): generatePromptHookScript(→progress), generateStopHookScript(→review), generateQuestionHookScript(→review) -- API route (src/app/api/hooks/status/route.ts): STATUS_MAP으로 string→TaskStatus 매핑 -- Board UI (src/components/Board.tsx:26-31): COLUMNS 배열로 컬럼 정의 -- TaskStatusBadge (src/components/TaskStatusBadge.tsx): 상태별 스타일 설정 -- CSS (src/app/globals.css:78-81): status 색상 변수 -- i18n (messages/*.json): columns 번역 키 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| src/entities/KanbanTask.ts | TaskStatus enum 정의 | Modify | -| src/migrations/*.ts | DB migration | Create | -| src/lib/database.ts | migration 등록 | Modify | -| src/lib/claudeHooksSetup.ts | Hook 스크립트 생성 | Modify | -| src/app/api/hooks/status/route.ts | Hook API endpoint | Modify | -| src/components/Board.tsx | 칸반 보드 UI | Modify | -| src/components/TaskStatusBadge.tsx | 상태 뱃지 스타일 | Modify | -| messages/ko.json | 한국어 번역 | Modify | -| messages/en.json | 영어 번역 | Modify | -| messages/zh.json | 중국어 번역 | Modify | -| src/app/globals.css | CSS 변수 | Modify | -| prd/design-system.json | 디자인 토큰 | Modify | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| Enum은 파일 분리 | BACKEND.md | enum은 별도 파일이지만, 기존 KanbanTask.ts에 이미 정의됨 → 유지 | -| Migration 후 database.ts에 등록 | CLAUDE.md | migrations 배열에 새 migration 클래스 import 추가 | -| CSS 변수 네이밍 | design-system.json | --color-status-{name} 패턴 | -| i18n 3개 파일 동시 수정 | CLAUDE.md | ko, en, zh 모두 동일 키로 번역 추가 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|------------| -| 새 상태 이름 | PENDING | 사용자 선택 | ASKING, DECISION | -| 컬럼 색상 | Purple (#8B5CF6) | 사용자 선택 | Orange, Red | -| Hook 변경 범위 | PreToolUse(AskUserQuestion)만 pending으로 변경 | Stop hook은 기존대로 review 유지 | - | - -## Data Models - -### TaskStatus Enum (변경) -| Value | DB Value | Description | -|-------|----------|-------------| -| TODO | "todo" | 할 일 | -| PROGRESS | "progress" | 진행 중 | -| **PENDING** | **"pending"** | **사용자 의사결정 대기** | -| REVIEW | "review" | 검토 대기 | -| DONE | "done" | 완료 | - -## Implementation Todos - -### Todo 1: TaskStatus enum에 PENDING 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: TaskStatus enum에 PENDING 값을 PROGRESS와 REVIEW 사이에 추가 -- **Work**: - - `src/entities/KanbanTask.ts`의 `TaskStatus` enum에 `PENDING = "pending"` 추가 (PROGRESS 다음, REVIEW 이전) -- **Convention Notes**: enum 값은 소문자 문자열 -- **Verification**: TypeScript 컴파일 에러 없음 -- **Exit Criteria**: TaskStatus.PENDING이 정상적으로 참조 가능 -- **Status**: pending - -### Todo 2: DB migration 생성 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: PostgreSQL의 kanban_tasks.status enum 타입에 'pending' 값을 추가 -- **Work**: - - `npm run migration:generate` 또는 수동으로 migration 파일 작성 - - PostgreSQL ALTER TYPE ... ADD VALUE 'pending' BEFORE 'review' 사용 - - `src/lib/database.ts`의 migrations 배열에 새 migration 클래스 import 추가 -- **Convention Notes**: migration 파일은 타임스탬프 기반 정렬, database.ts에 반드시 등록 -- **Verification**: `npm run migration:run` 성공 -- **Exit Criteria**: DB에 pending enum 값이 존재 -- **Status**: pending - -### Todo 3: Hook 스크립트 및 API 수정 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: AskUserQuestion hook이 pending 상태를 전송하도록 변경 -- **Work**: - - `src/lib/claudeHooksSetup.ts`의 `generateQuestionHookScript`: status를 "review" → "pending"으로 변경 - - `src/app/api/hooks/status/route.ts`의 `STATUS_MAP`에 `pending: TaskStatus.PENDING` 추가 -- **Convention Notes**: 기존 코드 스타일 유지 -- **Verification**: API에 `{"status": "pending"}` 전송 시 정상 처리 -- **Exit Criteria**: PreToolUse(AskUserQuestion) hook이 pending 상태를 전송하고, API가 이를 처리 -- **Status**: pending - -### Todo 4: UI 업데이트 (Board, TaskStatusBadge, CSS, 디자인 토큰) -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: 칸반 보드에 Pending 컬럼을 추가하고 시각적 스타일 적용 -- **Work**: - - `src/app/globals.css`: - - `:root`에 `--color-status-pending: #8B5CF6` 추가 - - `@theme inline`에 `--color-status-pending` 등록 - - `prd/design-system.json`에 status-pending 토큰 추가 - - `src/components/Board.tsx`의 `COLUMNS` 배열에 PENDING 컬럼 추가 (PROGRESS 다음, REVIEW 이전) - - `src/components/Board.tsx`의 `filteredTasks`에 `[TaskStatus.PENDING]: []` 추가 - - `src/components/TaskStatusBadge.tsx`의 `statusConfig`에 PENDING 스타일 추가 (purple 계열) -- **Convention Notes**: Tailwind CSS 변수 사용, bg-status-pending 클래스 사용 -- **Verification**: 보드에 Pending 컬럼이 표시되고 올바른 색상 적용 -- **Exit Criteria**: 5개 컬럼이 Todo → Progress → Pending → Review → Done 순서로 표시 -- **Status**: pending - -### Todo 5: i18n 번역 추가 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: 3개 언어 파일에 pending 컬럼 번역 추가 -- **Work**: - - `messages/ko.json`의 `board.columns`에 `"pending": "Pending"` 추가 - - `messages/en.json`의 `board.columns`에 `"pending": "Pending"` 추가 - - `messages/zh.json`의 `board.columns`에 `"pending": "待决定"` 추가 -- **Convention Notes**: 동일 키로 3개 파일 모두 업데이트 -- **Verification**: 각 locale에서 Pending 컬럼 이름이 올바르게 표시 -- **Exit Criteria**: ko, en, zh 번역 파일에 pending 키 존재 -- **Status**: pending - -## Verification Strategy -- TypeScript 컴파일: `npx tsc --noEmit` -- 빌드: `npm run build` -- DB migration 검토: 생성된 migration SQL 확인 - -## Progress Tracking -- Total Todos: 5 -- Completed: 5 -- Status: Execution complete - -## Change Log -- 2026-02-16: Plan created -- 2026-02-16: All todos completed, build verification passed diff --git a/.claude/plan/infra/2602/18-kanvibe-cli-script.plan.md b/.claude/plan/infra/2602/18-kanvibe-cli-script.plan.md deleted file mode 100644 index 6483766..0000000 --- a/.claude/plan/infra/2602/18-kanvibe-cli-script.plan.md +++ /dev/null @@ -1,93 +0,0 @@ -# KanVibe CLI Script (kanvibe start/stop) - -## Business Goal -기존 start.sh를 `bash kanvibe start` / `bash kanvibe stop` 형태의 통합 CLI 스크립트로 교체한다. 의존성 자동 체크 및 설치 프롬프트를 제공하고, 시스템 로케일 기반 i18n(ko/en/zh)을 적용하여 사용자 경험을 개선한다. - -## Scope -- **In Scope**: - - `kanvibe` bash 스크립트 생성 (start/stop 서브커맨드) - - 의존성 체크 (node 24+, pnpm, docker, git, tmux) + 선택 의존성 (zellij, gh) - - 미설치 의존성에 대한 설치 프롬프트 (Homebrew 기반) - - 시스템 로케일 자동 감지 i18n (ko/en/zh) - - 깔끔한 터미널 UI (색상, 아이콘, 진행 표시) - - PID 파일 기반 프로세스 관리 - - 기존 `start.sh` 삭제 -- **Out of Scope**: - - Linux 패키지 매니저 지원 (macOS Homebrew만) - - 자동 업데이트 기능 - - 데몬 모드 - -## Codebase Analysis Summary -프로젝트는 Next.js 16 커스텀 서버(server.ts)를 boot.js를 통해 실행한다. Docker로 PostgreSQL을 관리하며 TypeORM 마이그레이션을 사용한다. tmux와 zellij는 터미널 세션 관리에 사용되고, gh는 PR URL 자동 감지에 사용된다. - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `kanvibe` | 통합 CLI 스크립트 | Create | -| `start.sh` | 기존 시작 스크립트 | Delete | -| `package.json` | pnpm scripts | Reference | -| `docker-compose.yml` | DB 컨테이너 설정 | Reference | -| `.env.example` | 환경변수 참조 | Reference | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| 한국어 주석 | CLAUDE.md | 스크립트 내 주석은 한국어로 작성 | -| 환경변수 기본값 | .env.example, docker-compose.yml | KANVIBE_USER=admin, KANVIBE_PASSWORD=changeme, DB_PORT=4886 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 스크립트 형식 | bash 단일 파일 | 외부 의존성 없이 실행 | Python, Node.js | -| PID 관리 | `.kanvibe.pid` 파일 | 정확한 프로세스 종료 | pkill | -| i18n 구현 | bash 함수 + case 문 | bash 4+ 호환 | 별도 JSON 파싱 | -| 패키지 매니저 | Homebrew | macOS darwin 전용 | apt, yum | - -## Implementation Todos - -### Todo 1: kanvibe 스크립트 생성 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: `bash kanvibe start` / `bash kanvibe stop` 으로 실행 가능한 통합 CLI 스크립트 작성 -- **Work**: - - 프로젝트 루트에 `kanvibe` 파일 생성 - - i18n 함수: `detect_locale()` — $LANG에서 ko/en/zh 감지, 기본 en - - i18n 메시지: `msg()` 함수 — 키 기반으로 현재 로케일 메시지 반환 - - 의존성 체크 함수: `check_deps()` — node(24+), pnpm, docker, git, tmux 필수 체크 + zellij, gh 선택 - - 의존성 설치 함수: `install_dep()` — Homebrew로 개별 설치, 설치 전 확인 프롬프트 - - `start` 서브커맨드: 의존성 체크 → pnpm install → docker compose up -d db → DB 대기 → 마이그레이션 → 빌드 → PID 기록 → 앱 시작 - - `stop` 서브커맨드: PID 파일로 앱 종료 → docker compose down - - 터미널 UI: 색상(ANSI), 체크마크/엑스 아이콘, 단계 표시 - - `chmod +x kanvibe` -- **Convention Notes**: 주석 한국어, 환경변수 기본값은 .env.example과 동일 -- **Verification**: `bash kanvibe` 실행 시 사용법 출력 확인 -- **Exit Criteria**: `bash kanvibe start`가 의존성 체크 후 서버 시작, `bash kanvibe stop`이 서버+DB 종료 -- **Status**: pending - -### Todo 2: start.sh 삭제 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: 기존 start.sh 파일 제거 -- **Work**: - - `start.sh` 파일 삭제 -- **Convention Notes**: 없음 -- **Verification**: start.sh 파일이 존재하지 않음 -- **Exit Criteria**: 파일 삭제 완료 -- **Status**: pending - -## Verification Strategy -- `bash kanvibe` 실행 시 사용법 메시지 출력 -- `bash kanvibe start` 실행 시 의존성 체크 UI 표시 -- `bash kanvibe stop` 실행 시 프로세스 종료 동작 -- shellcheck 또는 bash -n 문법 검증 - -## Progress Tracking -- Total Todos: 2 -- Completed: 2 -- Status: Execution complete - -## Change Log -- 2026-02-18: Plan created -- 2026-02-18: Todo 1 completed — kanvibe script created with i18n, dep check, start/stop -- 2026-02-18: Todo 2 completed — start.sh deleted -- 2026-02-18: Additional — .kanvibe.pid added to .gitignore diff --git a/.claude/plan/infra/2602/21-git-exclude-ai-hooks.plan.md b/.claude/plan/infra/2602/21-git-exclude-ai-hooks.plan.md deleted file mode 100644 index 561e58e..0000000 --- a/.claude/plan/infra/2602/21-git-exclude-ai-hooks.plan.md +++ /dev/null @@ -1,114 +0,0 @@ -# AI Hooks Git Exclude 설정 - -## Business Goal - -worktree에 자동 생성되는 AI 코딩 도구(Claude, Gemini, Codex, OpenCode)의 hooks 파일들이 git tracking에 포함되지 않도록 `.git/info/excluded`에 패턴을 추가한다. `.gitignore`는 커밋되므로 로컬 전용 제외에 적합한 `info/excluded`를 사용한다. - -## Scope - -- **In Scope**: `gitExclude.ts` 유틸리티 생성, 4개 hooks setup 함수에서 호출 추가 -- **Out of Scope**: 기존 테스트 수정 (유틸은 best-effort로 실패해도 hooks 설치에 영향 없음) - -## Codebase Analysis Summary - -4개 AI 도구의 hooks setup 함수가 각각 worktree 경로에 설정 파일을 생성한다. 현재 이 파일들은 git exclude 처리가 없어 `git status`에 untracked로 노출된다. - -### Relevant Files - -| File | Role | Action | -|------|------|--------| -| `src/lib/gitExclude.ts` | git exclude 패턴 추가 유틸리티 | Create | -| `src/lib/claudeHooksSetup.ts` | Claude hooks 설치 | Modify | -| `src/lib/geminiHooksSetup.ts` | Gemini hooks 설치 | Modify | -| `src/lib/codexHooksSetup.ts` | Codex hooks 설치 | Modify | -| `src/lib/openCodeHooksSetup.ts` | OpenCode hooks 설치 | Modify | - -### 제외 대상 파일/디렉토리 - -| Tool | 생성 파일 | Exclude 패턴 | -|------|----------|-------------| -| Claude | `.claude/hooks/*.sh`, `.claude/settings.json` | `.claude/hooks/`, `.claude/settings.json` | -| Gemini | `.gemini/hooks/*.sh`, `.gemini/settings.json` | `.gemini/hooks/`, `.gemini/settings.json` | -| Codex | `.codex/hooks/*.sh`, `.codex/config.toml` | `.codex/hooks/`, `.codex/config.toml` | -| OpenCode | `.opencode/plugins/kanvibe-plugin.ts` | `.opencode/plugins/` | - -### Conventions to Follow - -| Convention | Source | Rule | -|-----------|--------|------| -| 한국어 주석 | CODE_PRINCIPLES.md | JSDoc 주석은 한국어로 | -| KISS | CODE_PRINCIPLES.md | 함수 하나가 한 가지 일 | -| 에러 처리 | 기존 hooks setup 패턴 | try-catch로 감싸되 실패해도 메인 로직 중단 안 함 | - -## Architecture Decisions - -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| exclude 위치 | `.git/info/excluded` | 로컬 전용, 커밋 불필요 | `.gitignore` (커밋됨), `~/.config/git/ignore` (전역) | -| git dir 탐색 | `git rev-parse --git-dir` | worktree에서도 정확한 git dir 반환 | path 직접 조합 (worktree 구조 가정 필요) | -| 호출 위치 | 각 setup 함수 내부 | 설치와 exclude가 항상 쌍으로 실행됨 | 외부 호출자에서 별도 호출 (누락 가능) | -| 패턴 범위 | 4개 도구 전부 한번에 추가 | 어떤 도구든 먼저 설치되면 전체 exclude 보장 | 각 도구별 자기 패턴만 (불완전) | - -## Implementation Todos - -### Todo 1: gitExclude 유틸리티 생성 - -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: `addAiToolPatternsToGitExclude(repoPath)` 함수를 생성하여 `.git/info/excluded`에 AI 도구 패턴을 멱등적으로 추가한다 -- **Work**: - - `src/lib/gitExclude.ts` 파일 생성 - - `addAiToolPatternsToGitExclude(repoPath: string): Promise` 함수 구현 - - `execAsync("git -C rev-parse --git-dir")`로 git 디렉토리 경로 획득 - - `/info/excluded` 파일을 읽고 (없으면 빈 문자열) - - `# KanVibe AI hooks (auto-generated)` 마커 블록이 없으면 패턴 블록 전체를 append - - 이미 마커가 있으면 skip (멱등성) - - `info/` 디렉토리가 없으면 `mkdir -p`로 생성 -- **Convention Notes**: JSDoc 한국어, export async function -- **Verification**: 유틸리티 파일 생성 확인, TypeScript 컴파일 오류 없음 -- **Exit Criteria**: `src/lib/gitExclude.ts`가 존재하고 export 함수가 올바르게 정의됨 -- **Status**: pending - -### Todo 2: 4개 hooks setup 함수에 gitExclude 호출 추가 - -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: 각 setup 함수가 hooks 파일 생성 후 git exclude 패턴을 자동 추가하도록 한다 -- **Work**: - - `src/lib/claudeHooksSetup.ts`의 `setupClaudeHooks` 함수 마지막에 `await addAiToolPatternsToGitExclude(repoPath)` 호출 추가 (try-catch로 감싸서 실패 시 console.error만) - - `src/lib/geminiHooksSetup.ts`의 `setupGeminiHooks` 함수 마지막에 동일하게 추가 - - `src/lib/codexHooksSetup.ts`의 `setupCodexHooks` 함수 마지막에 동일하게 추가 - - `src/lib/openCodeHooksSetup.ts`의 `setupOpenCodeHooks` 함수 마지막에 동일하게 추가 - - 각 파일 상단에 `import { addAiToolPatternsToGitExclude } from "@/lib/gitExclude"` 추가 -- **Convention Notes**: 에러가 발생해도 hooks 설치 자체는 성공한 상태이므로 throw하지 않음 -- **Verification**: TypeScript 컴파일 오류 없음, 기존 테스트 통과 -- **Exit Criteria**: 4개 setup 함수 모두에 gitExclude 호출이 추가됨 -- **Status**: pending - -### Todo 3: 빌드 및 테스트 검증 - -- **Priority**: 3 -- **Dependencies**: Todo 2 -- **Goal**: 변경 사항이 기존 코드를 깨뜨리지 않음을 확인한다 -- **Work**: - - `NODE_ENV=test pnpm test` 실행하여 기존 테스트 통과 확인 - - TypeScript 컴파일 검증 -- **Verification**: 테스트 통과, 빌드 성공 -- **Exit Criteria**: 모든 테스트 green, 컴파일 에러 없음 -- **Status**: pending - -## Verification Strategy - -- TypeScript 컴파일: `pnpm build` 또는 `npx tsc --noEmit` -- 기존 테스트: `NODE_ENV=test pnpm test` -- 통합 확인: setup 함수 호출 시 `.git/info/excluded`에 패턴이 추가되는지 수동 확인 - -## Progress Tracking - -- Total Todos: 3 -- Completed: 0 -- Status: Planning complete - -## Change Log - -- 2026-02-21: Plan created diff --git a/.claude/plan/notification/2602/17-browser-notification-on-hooks-status.plan.md b/.claude/plan/notification/2602/17-browser-notification-on-hooks-status.plan.md deleted file mode 100644 index e4d829b..0000000 --- a/.claude/plan/notification/2602/17-browser-notification-on-hooks-status.plan.md +++ /dev/null @@ -1,121 +0,0 @@ -# Hooks 상태 변경 시 브라우저 알림 - -## Business Goal -Claude Code hooks가 task 상태를 변경할 때 브라우저 알림을 발송하여, 사용자가 KanVibe 보드를 보고 있지 않아도 AI 에이전트의 작업 상태 변화를 즉시 인지할 수 있도록 한다. - -## Scope -- **In Scope**: `/api/hooks/status` API 경유 상태 변경 시 Browser Notification 발송 -- **Out of Scope**: 수동 드래그앤드롭/UI 상태 변경 알림, 알림 설정 UI, 사운드/진동 - -## Codebase Analysis Summary -- Claude Code hooks (`src/lib/claudeHooksSetup.ts`)가 `/api/hooks/status`로 POST 요청 -- `/api/hooks/status/route.ts`에서 DB 업데이트 후 `broadcastBoardUpdate()` 호출 -- `broadcastBoardUpdate()` (`src/lib/boardNotifier.ts`)가 WebSocket으로 `{ type: "board-updated" }` 전송 -- `useAutoRefresh` (`src/hooks/useAutoRefresh.ts`)가 WebSocket 메시지 수신 시 `router.refresh()` 호출 -- 기존 WebSocket: port+10000에서 운영, `boardClients` Set으로 클라이언트 관리 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/lib/boardNotifier.ts` | WebSocket 브로드캐스트 | Modify — 상세 정보 포함 브로드캐스트 함수 추가 | -| `src/app/api/hooks/status/route.ts` | hooks 상태 변경 API | Modify — task 상세 정보 브로드캐스트 | -| `src/hooks/useTaskNotification.ts` | 브라우저 알림 훅 | Create | -| `src/hooks/useAutoRefresh.ts` | WebSocket 연결 관리 | Modify — 알림 메시지 처리 추가 | -| `src/components/Board.tsx` | 메인 보드 컴포넌트 | Modify — 알림 훅 통합 | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| 한국어 주석 | CODE_PRINCIPLES.md | 주석/답변은 한국어 | -| "use client" | project-architecture | 클라이언트 컴포넌트에 디렉티브 필수 | -| 훅 위치 | 프로젝트 구조 | `src/hooks/` 디렉토리 | -| Boolean 네이밍 | CODE_PRINCIPLES.md | `is`, `has`, `can`, `should` 접두사 | -| KISS | CODE_PRINCIPLES.md | 단순하고 명확한 코드, 불필요한 복잡성 없음 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 알림 트리거 | hooks API 경유 시에만 | 사용자 요청 범위 | 모든 상태 변경 | -| 전달 채널 | 기존 WebSocket 확장 | 인프라 재사용, 추가 의존성 없음 | SSE, polling | -| 메시지 분리 | 새 타입 `task-status-changed` | 기존 `board-updated` 동작 유지 | 기존 타입에 data 추가 | -| 권한 요청 시점 | Board 마운트 시 | 사용자가 보드 진입 시 자연스럽게 요청 | 설정 페이지 | - -## Implementation Todos - -### Todo 1: boardNotifier에 상세 브로드캐스트 함수 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: WebSocket으로 task 상태 변경 상세 정보를 전송할 수 있도록 한다 -- **Work**: - - `src/lib/boardNotifier.ts`에 `TaskStatusChangedPayload` 인터페이스 추가: `{ projectName: string; branchName: string; taskTitle: string; description: string | null; newStatus: string }` - - `broadcastTaskStatusChanged(payload: TaskStatusChangedPayload)` 함수 추가 - - 메시지 형식: `{ type: "task-status-changed", ...payload }` -- **Convention Notes**: export 함수, 한국어 JSDoc 주석 -- **Verification**: TypeScript 컴파일 통과 -- **Exit Criteria**: `broadcastTaskStatusChanged` 함수가 정상 export됨 -- **Status**: pending - -### Todo 2: hooks/status API에서 상세 브로드캐스트 호출 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: hooks API가 상태 변경 시 task 상세 정보를 WebSocket으로 브로드캐스트한다 -- **Work**: - - `src/app/api/hooks/status/route.ts`에서 `broadcastTaskStatusChanged` import - - `broadcastBoardUpdate()` 호출 직후 `broadcastTaskStatusChanged()` 호출 - - payload: `{ projectName, branchName, taskTitle: task.title, description: task.description, newStatus: taskStatus }` -- **Convention Notes**: 기존 `broadcastBoardUpdate()` 호출은 그대로 유지 -- **Verification**: TypeScript 컴파일 통과, API 동작 확인 -- **Exit Criteria**: hooks API가 두 가지 브로드캐스트 모두 수행 - -### Todo 3: useTaskNotification 커스텀 훅 생성 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: Browser Notification API를 캡슐화한 커스텀 훅을 만든다 -- **Work**: - - `src/hooks/useTaskNotification.ts` 파일 생성 - - `useTaskNotification()` 훅 구현: - - 마운트 시 `Notification.requestPermission()` 호출 - - `notifyTaskStatusChanged(payload)` 콜백 함수 반환 - - 알림 title: `"{projectName} — {branchName}"` - - 알림 body: `"{taskTitle}: {newStatus}로 변경"` (description이 있으면 함께 표시) - - `Notification.permission === "granted"` 일 때만 발송 -- **Convention Notes**: `"use client"` 디렉티브, 한국어 주석, `is`/`has` 접두사로 boolean -- **Verification**: TypeScript 컴파일 통과 -- **Exit Criteria**: 훅이 정상 export되고 Notification API 호출 로직이 포함됨 - -### Todo 4: useAutoRefresh에 알림 통합 -- **Priority**: 2 -- **Dependencies**: Todo 1, Todo 3 -- **Goal**: WebSocket에서 `task-status-changed` 메시지 수신 시 브라우저 알림을 발송한다 -- **Work**: - - `src/hooks/useAutoRefresh.ts`에서 `useTaskNotification` import - - `ws.onmessage` 핸들러에 `task-status-changed` 타입 처리 추가 - - `notifyTaskStatusChanged(data)` 호출 -- **Convention Notes**: 기존 `board-updated` 처리 로직은 그대로 유지 -- **Verification**: TypeScript 컴파일 통과 -- **Exit Criteria**: WebSocket 메시지 수신 시 브라우저 알림이 트리거됨 - -### Todo 5: 빌드 검증 -- **Priority**: 3 -- **Dependencies**: Todo 2, Todo 4 -- **Goal**: 전체 빌드가 정상 통과하는지 확인한다 -- **Work**: - - `pnpm build` 실행 - - TypeScript 에러 및 lint 에러 확인 - - 에러 발생 시 수정 -- **Convention Notes**: N/A -- **Verification**: `pnpm build` 성공 -- **Exit Criteria**: 빌드 에러 없음 - -## Verification Strategy -- `pnpm build` 통과 -- 코드 리뷰: 기존 `board-updated` 동작이 그대로 유지되는지 확인 -- 알림 흐름: hooks API → broadcastTaskStatusChanged → WebSocket → useAutoRefresh → useTaskNotification → Browser Notification - -## Progress Tracking -- Total Todos: 5 -- Completed: 0 -- Status: Planning complete - -## Change Log -- 2026-02-17: Plan created diff --git a/.claude/plan/notification/2602/17-notification-redirect-icon.plan.md b/.claude/plan/notification/2602/17-notification-redirect-icon.plan.md deleted file mode 100644 index 6a27b0e..0000000 --- a/.claude/plan/notification/2602/17-notification-redirect-icon.plan.md +++ /dev/null @@ -1,222 +0,0 @@ -# Chrome 알림 클릭 시 작업 상세 페이지로 Redirect - -## Business Goal -사용자가 Chrome 브라우저 알림을 클릭할 때 자동으로 해당 작업의 상세 페이지로 이동하도록 구현하고, KanVibe 로고를 알림 아이콘으로 표시하여 브랜드 인식을 높인다. - -## Scope -- **In Scope**: - - Service Worker 등록 및 notificationclick 핸들러 구현 - - useTaskNotification 훅 업그레이드 (redirectUrl 및 로고 아이콘 추가) - - broadcastTaskStatusChanged 호출 시 URL 정보 포함 - - 모든 locale에 대해 동적으로 redirect URL 생성 - - 기존 알림 설정 UI와 통합 -- **Out of Scope**: - - 큰 배경 이미지 (image 속성) - - 사운드/진동 추가 - - 알림 액션 버튼 - -## Codebase Analysis Summary - -### 기존 알림 아키텍처 -- **WebSocket**: `port+10000` 에서 운영, 클라이언트 Set으로 관리 -- **NotificationListener.tsx**: layout.tsx에서 마운트, WebSocket 연결 + 필터링 -- **useTaskNotification.ts**: Browser Notification API 캡슐화 -- **useAutoRefresh.ts**: WebSocket 메시지 수신 (board-updated, task-status-changed) -- **broadcastTaskStatusChanged**: boardNotifier.ts에서 구현 - -### 기존 코드 흐름 -``` -hooks API (status 변경) - → broadcastTaskStatusChanged() - → WebSocket { type: "task-status-changed", projectName, branchName, taskTitle, description, newStatus } - → useAutoRefresh (수신) - → notifyTaskStatusChanged() (Browser Notification API 호출) -``` - -### 로도 및 라우팅 -- 로고 위치: `public/logo.png` (또는 검증 후 결정) -- i18n 라우팅: `/[locale]/task/[id]` (locale 동적) -- 현재 locale: `usePathname()` 또는 `getLocale()` 사용 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `public/sw.js` | Service Worker | Create — notificationclick 핸들러 | -| `src/hooks/useTaskNotification.ts` | 알림 발송 훅 | Modify — redirectUrl, icon 매개변수 추가 | -| `src/lib/boardNotifier.ts` | WebSocket 브로드캐스트 | Modify — taskId 필드 추가 | -| `src/app/api/hooks/status/route.ts` | Hooks API | Modify — broadcastTaskStatusChanged 호출 시 taskId 전달 | -| `src/hooks/useAutoRefresh.ts` | WebSocket 연결 관리 | Modify — task-status-changed 메시지 처리 시 redirectUrl 포함 | -| `src/app/[locale]/layout.tsx` | 루트 레이아웃 | Modify — Service Worker 등록 코드 추가 | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| 한국어 주석 | CODE_PRINCIPLES.md | 주석/설명은 한국어 | -| "use client" | project-architecture | 클라이언트 컴포넌트에 필수 | -| Boolean 네이밍 | CODE_PRINCIPLES.md | `is`, `has`, `can`, `should` 접두사 | -| 훅 위치 | project-architecture | `src/hooks/` 디렉토리 | -| 정적 자산 | Next.js 표준 | `public/` 디렉토리 | -| Service Worker | MDN | `public/sw.js`에 등록 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| Service Worker 위치 | `public/sw.js` | Next.js 표준 위치, 쉬운 관리 | 앱 내부 생성 | -| 등록 시점 | layout.tsx (Client Component) | 앱 초기화 시 한 번만 실행 | 다른 컴포넌트에서 | -| 로고 경로 | `public/logo.png` | 정적 자산 재사용 | 동적 URL 생성 | -| URL 생성 방식 | notification.data.taskId 사용 | locale-aware 동적 라우팅 | 저장된 redirectUrl | -| WebSocket 메시지 | taskId 필드 추가 | redirect URL 동적 생성 | URL 직접 저장 (locale 고정) | -| 기존 코드 영향 | 최소화 | 새 Service Worker 추가, 기존 훅 확장만 | 전체 리팩토링 | - -## Implementation Todos - -### Todo 1: Service Worker 파일 생성 및 등록 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: notificationclick 이벤트를 처리하는 Service Worker를 작성하고 등록한다 -- **Work**: - - `public/sw.js` 파일 생성 - - `self.addEventListener('notificationclick', (event) => { ... })` 핸들러 구현 - - `event.notification.data.taskId`에서 taskId 추출 - - `const locale = new URL(self.location).searchParams.get('locale') || 'ko'` 로 locale 획득 - - `clients.openWindow(`/${locale}/task/${taskId}`)` 호출 - - `event.notification.close()` 호출 - - `src/app/[locale]/layout.tsx`에 Service Worker 등록 코드 추가: - ```typescript - useEffect(() => { - if ('serviceWorker' in navigator && typeof window !== 'undefined') { - navigator.serviceWorker.register('/sw.js?locale=' + locale).catch(err => console.warn('SW 등록 실패', err)); - } - }, [locale]); - ``` -- **Convention Notes**: 한국어 주석, 에러 처리는 console.warn으로 무시 -- **Verification**: TypeScript 컴파일 통과, 브라우저 콘솔에서 Service Worker 등록 확인 -- **Exit Criteria**: Service Worker가 정상 등록되고, notification.json에 예제 객체가 생성됨 -- **Status**: pending - -### Todo 2: boardNotifier의 TaskStatusChangedPayload에 taskId 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: WebSocket 브로드캐스트 메시지에 taskId를 포함시킨다 -- **Work**: - - `src/lib/boardNotifier.ts`의 `TaskStatusChangedPayload` 인터페이스 업데이트: - ```typescript - export interface TaskStatusChangedPayload { - projectName: string; - branchName: string; - taskTitle: string; - description: string | null; - newStatus: string; - taskId: string; // 추가 - } - ``` - - `broadcastTaskStatusChanged` 함수는 그대로 유지 (payload 전달만 하면 됨) -- **Convention Notes**: 기존 구조 유지, 타입 안전성 확보 -- **Verification**: TypeScript 컴파일 통과 -- **Exit Criteria**: 인터페이스에 taskId 필드가 추가됨 -- **Status**: pending - -### Todo 3: useTaskNotification 훅 업그레이드 (redirectUrl, icon 추가) -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: Browser Notification 발송 시 redirectUrl과 로고 아이콘을 포함한다 -- **Work**: - - `src/hooks/useTaskNotification.ts` 파일 수정: - - `notifyTaskStatusChanged` 함수 시그니처 업데이트: - ```typescript - notifyTaskStatusChanged(payload: TaskStatusChangedPayload & { taskId: string }) - ``` - - `new Notification(title, { ..., data: { taskId: payload.taskId }, icon: '/logo.png' })` - - 로고 아이콘은 항상 포함 (모든 브라우저 지원) -- **Convention Notes**: "use client" 디렉티브 유지, 한국어 주석 -- **Verification**: TypeScript 컴파일 통과, 브라우저 DevTools에서 notification 객체 확인 -- **Exit Criteria**: 훅이 taskId를 data 필드에 포함시키고, icon 속성이 설정됨 -- **Status**: pending - -### Todo 4: useAutoRefresh 업데이트 (task-status-changed 메시지 처리) -- **Priority**: 2 -- **Dependencies**: Todo 2, Todo 3 -- **Goal**: WebSocket에서 수신한 task-status-changed 메시지를 처리하여 알림을 발송한다 -- **Work**: - - `src/hooks/useAutoRefresh.ts`에서 `ws.onmessage` 핸들러 수정: - - `if (data.type === 'task-status-changed')` 블록 추가 - - `const notifyTaskStatusChanged = useTaskNotification()` 로 훅 호출 - - `notifyTaskStatusChanged(data)` 호출 (taskId 포함) - - 기존 `board-updated` 처리는 그대로 유지 -- **Convention Notes**: 기존 핸들러 구조 유지, 타입 안전성 -- **Verification**: TypeScript 컴파일 통과 -- **Exit Criteria**: task-status-changed 메시지가 수신될 때 알림이 발송됨 -- **Status**: pending - -### Todo 5: hooks/status API에서 taskId 전달 확인 -- **Priority**: 2 -- **Dependencies**: Todo 2 -- **Goal**: `/api/hooks/status` route에서 broadcastTaskStatusChanged 호출 시 taskId를 포함한다 -- **Work**: - - `src/app/api/hooks/status/route.ts` 파일 확인 - - `broadcastTaskStatusChanged()` 호출 시 payload에 `taskId: task.id` 포함 확인 - - 기존 코드가 이미 포함하고 있으면 변경 불필요 -- **Convention Notes**: 기존 구현 우선, 필요시만 수정 -- **Verification**: 코드 리뷰 -- **Exit Criteria**: taskId가 payload에 포함됨 -- **Status**: pending - -### Todo 6: 기존 알림 설정 UI와 통합 확인 -- **Priority**: 2 -- **Dependencies**: Todo 1, Todo 3 -- **Goal**: 기존 알림 필터 설정이 redirect 기능과 함께 동작하는지 확인한다 -- **Work**: - - `src/components/NotificationListener.tsx`에서 useTaskNotification 훅 호출 확인 - - 알림 설정(isNotificationEnabled, enabledStatuses)이 여전히 필터링 역할 수행 확인 - - 기존 Board.tsx → ProjectSettings 데이터 흐름 확인 - - 변경 불필요하면 스킵 -- **Convention Notes**: 기존 로직 보존 -- **Verification**: 코드 리뷰 -- **Exit Criteria**: 기존 필터 동작이 유지됨 -- **Status**: pending - -### Todo 7: 빌드 및 테스트 -- **Priority**: 3 -- **Dependencies**: Todo 4, Todo 5, Todo 6 -- **Goal**: 전체 빌드가 정상 통과하고 기능이 정상 동작하는지 확인한다 -- **Work**: - - `pnpm build` 실행 - - TypeScript 에러 및 lint 에러 확인 - - 에러 발생 시 수정 - - 수동 테스트: - - 작업 상태 변경 (Hooks API 또는 드래그앤드롭) - - 알림 발송 확인 - - 알림 클릭 → 작업 상세 페이지로 이동 확인 - - 로고 아이콘 표시 확인 -- **Convention Notes**: N/A -- **Verification**: `pnpm build` 성공, 수동 기능 테스트 -- **Exit Criteria**: 빌드 에러 없음, 기능 동작 확인 -- **Status**: pending - -## Verification Strategy -- **빌드 검증**: `pnpm build` 성공 -- **TypeScript 타입**: 모든 payloads에 taskId 포함 확인 -- **Service Worker 등록**: 브라우저 DevTools → Application → Service Workers에서 등록 확인 -- **알림 클릭 동작**: - - 알림 발송 확인 - - 알림 클릭 시 `/[locale]/task/[id]` 페이지로 이동 확인 - - 로고 아이콘 표시 확인 -- **기존 기능 보존**: - - 기존 알림 필터(TODO, PROGRESS 등)가 여전히 동작 - - board-updated WebSocket 메시지 처리 그대로 유지 - -## Progress Tracking -- Total Todos: 7 -- Completed: 7 -- Status: Execution complete ✅ - -## Change Log -- 2026-02-17: Plan created (사용자 승인 후) -- 2026-02-17: All todos executed and verified - - Todo 1: Service Worker 파일 생성 (public/sw.js) ✅ - - Todo 2: TaskStatusChangedPayload에 taskId 추가 ✅ - - Todo 3: useTaskNotification 훅 업그레이드 (icon, data fields) ✅ - - Todo 4: NotificationListener에서 task-status-changed 메시지 처리 ✅ - - Todo 5: hooks/status API에서 taskId 전달 ✅ - - Todo 6: 기존 알림 설정 UI와 통합 확인 ✅ - - Todo 7: 모든 테스트 통과 (54 tests passed) ✅ diff --git a/.claude/plan/notification/2602/17-notification-settings-ui.plan.md b/.claude/plan/notification/2602/17-notification-settings-ui.plan.md deleted file mode 100644 index 3281276..0000000 --- a/.claude/plan/notification/2602/17-notification-settings-ui.plan.md +++ /dev/null @@ -1,138 +0,0 @@ -# 알림 설정 UI 구현 - -## Business Goal -사용자가 Chrome 브라우저 알림을 세밀하게 제어할 수 있도록 한다. 알림 전역 ON/OFF와 상태별 필터링(TODO, PROGRESS, PENDING, REVIEW, DONE)을 설정하여, 원하는 상태 변경에 대해서만 알림을 받을 수 있게 한다. - -## Scope -- **In Scope**: 알림 전역 토글, 상태별 필터 체크박스, ProjectSettings 패널 내 UI, AppSettings DB 저장, NotificationListener 필터링, 3개 언어 번역 -- **Out of Scope**: 사운드/진동, 프로젝트별 필터링, 알림 히스토리, 알림 커스텀 메시지 - -## Codebase Analysis Summary -- 기존 알림 시스템: WebSocket → NotificationListener → useTaskNotification → Browser Notification -- `AppSettings` 엔티티: key-value 패턴, `getAppSetting`/`setAppSetting` 헬퍼 존재 -- `ProjectSettings.tsx`: 기존 설정 패널, toggle switch 패턴(`role="switch"`, `aria-checked`) 존재 -- `NotificationListener.tsx`: layout.tsx에서 마운트, WebSocket 연결 및 알림 발송 -- `useTaskNotification.ts`: Browser Notification API 캡슐화 -- TaskStatus: TODO, PROGRESS, PENDING, REVIEW, DONE - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/app/actions/appSettings.ts` | AppSettings CRUD | Modify — 알림 설정 getter/setter 추가 | -| `src/components/ProjectSettings.tsx` | 설정 패널 UI | Modify — 알림 설정 섹션 추가 | -| `src/components/NotificationListener.tsx` | 알림 수신 | Modify — 설정 props 받아 필터링 적용 | -| `src/hooks/useTaskNotification.ts` | 알림 발송 훅 | Modify — 상태 필터링 로직 추가 | -| `src/app/[locale]/layout.tsx` | 루트 레이아웃 | Modify — 알림 설정을 NotificationListener에 전달 | -| `messages/ko.json` | 한국어 번역 | Modify — 알림 설정 키 추가 | -| `messages/en.json` | 영어 번역 | Modify — 알림 설정 키 추가 | -| `messages/zh.json` | 중국어 번역 | Modify — 알림 설정 키 추가 | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| 한국어 주석 | CODE_PRINCIPLES.md | 주석/답변은 한국어 | -| "use client" | project-architecture | 클라이언트 컴포넌트에 디렉티브 필수 | -| Boolean 네이밍 | CODE_PRINCIPLES.md | `is`, `has`, `can`, `should` 접두사 | -| AppSettings 패턴 | appSettings.ts | `getAppSetting`/`setAppSetting` 사용 | -| Toggle 패턴 | ProjectSettings.tsx | `role="switch"`, `aria-checked` | -| i18n | CLAUDE.md | 3개 언어 동시 번역, `useTranslations("settings")` | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 저장소 | AppSettings key-value | 기존 패턴 재사용, migration 불필요 | 전용 Entity | -| 키 구조 | `notification_enabled` + `notification_statuses` | 단순하고 확장 가능 | 단일 JSON | -| 설정 전달 | layout → props → NotificationListener | `revalidatePath`로 자동 반영, 기존 패턴과 일관 | API fetch | -| 기본값 | 전체 활성화 | 기존 동작 유지 (backward compatible) | 전체 비활성화 | - -## Implementation Todos - -### Todo 1: AppSettings에 알림 설정 server action 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 알림 설정을 DB에서 읽고 쓸 수 있는 server action을 추가한다 -- **Work**: - - `src/app/actions/appSettings.ts`에 상수 추가: `NOTIFICATION_ENABLED_KEY = "notification_enabled"`, `NOTIFICATION_STATUSES_KEY = "notification_statuses"` - - `getNotificationSettings()` 함수 추가: `{ isEnabled: boolean, enabledStatuses: string[] }` 반환. 키 없으면 기본값 `{ isEnabled: true, enabledStatuses: ["todo","progress","pending","review","done"] }` - - `setNotificationEnabled(enabled: boolean)` 함수 추가: `setAppSetting` 사용 + `revalidatePath("/")` - - `setNotificationStatuses(statuses: string[])` 함수 추가: JSON.stringify로 저장 + `revalidatePath("/")` -- **Convention Notes**: `"use server"` 디렉티브, 한국어 주석, 기존 `setSidebarDefaultCollapsed` 패턴 따름 -- **Verification**: TypeScript 컴파일 통과 -- **Exit Criteria**: 4개 함수가 정상 export됨 -- **Status**: pending - -### Todo 2: i18n 번역 키 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 알림 설정 UI에 필요한 번역 키를 3개 언어에 추가한다 -- **Work**: - - `messages/ko.json`의 `settings` 객체에 추가: - - `"notificationSection"`: `"알림"` - - `"notificationEnabled"`: `"브라우저 알림"` - - `"notificationEnabledDescription"`: `"작업 상태 변경 시 브라우저 알림을 받습니다."` - - `"notificationStatusFilter"`: `"알림 받을 상태"` - - `"notificationStatusFilterDescription"`: `"선택한 상태로 변경될 때만 알림을 받습니다."` - - `messages/en.json`의 `settings` 객체에 동일 키로 영어 번역 추가 - - `messages/zh.json`의 `settings` 객체에 동일 키로 중국어 번역 추가 -- **Convention Notes**: 3개 언어 동시 추가, 기존 키 네이밍 패턴(`camelCase`) 따름 -- **Verification**: JSON 파싱 정상 -- **Exit Criteria**: 3개 파일에 5개 키씩 추가됨 -- **Status**: pending - -### Todo 3: NotificationListener에 설정 props 추가 및 필터링 적용 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: NotificationListener가 알림 설정을 props로 받아 필터링을 적용한다 -- **Work**: - - `src/components/NotificationListener.tsx`에 props 인터페이스 추가: `{ isNotificationEnabled: boolean; enabledStatuses: string[] }` - - `useTaskNotification` 훅에 `enabledStatuses` 전달 - - `src/hooks/useTaskNotification.ts`의 `notifyTaskStatusChanged`에 필터링 추가: `isNotificationEnabled`가 false이면 리턴, `enabledStatuses`에 포함되지 않으면 리턴 - - `src/app/[locale]/layout.tsx`에서 `getNotificationSettings()` 호출하여 props 전달 -- **Convention Notes**: `"use client"` 디렉티브 유지, Boolean prop은 `is` 접두사 -- **Verification**: TypeScript 컴파일 통과 -- **Exit Criteria**: 설정에 따라 알림이 필터링됨 -- **Status**: pending - -### Todo 4: ProjectSettings에 알림 설정 UI 섹션 추가 -- **Priority**: 2 -- **Dependencies**: Todo 1, Todo 2 -- **Goal**: ProjectSettings 패널에 알림 설정 섹션을 추가한다 -- **Work**: - - `src/components/ProjectSettings.tsx`에 props 추가: `notificationSettings: { isEnabled: boolean; enabledStatuses: string[] }` - - "상세 페이지" 섹션 아래에 "알림" 섹션 추가 - - 전역 토글: 기존 `sidebarDefaultCollapsed` 토글 패턴 재사용 (`role="switch"`, `aria-checked`) - - 상태별 체크박스: TODO, PROGRESS, PENDING, REVIEW, DONE 5개 체크박스 - - 전역 토글 OFF 시 체크박스 영역 비활성화 (opacity + pointer-events) - - `startTransition` 내에서 `setNotificationEnabled`, `setNotificationStatuses` 호출 - - Board.tsx에서 notificationSettings를 ProjectSettings에 전달 -- **Convention Notes**: 기존 toggle 패턴, `useTranslations("settings")`, Tailwind CSS 변수 -- **Verification**: TypeScript 컴파일 통과, UI 렌더링 정상 -- **Exit Criteria**: 토글과 체크박스가 정상 동작하고 DB에 저장됨 -- **Status**: pending - -### Todo 5: 빌드 검증 -- **Priority**: 3 -- **Dependencies**: Todo 3, Todo 4 -- **Goal**: 전체 빌드가 정상 통과하는지 확인한다 -- **Work**: - - `pnpm build` 실행 - - TypeScript 에러 및 lint 에러 확인 - - 에러 발생 시 수정 -- **Convention Notes**: N/A -- **Verification**: `pnpm build` 성공 -- **Exit Criteria**: 빌드 에러 없음 -- **Status**: pending - -## Verification Strategy -- `pnpm build` 통과 -- 알림 설정 흐름: ProjectSettings → Server Action → DB → layout.tsx → NotificationListener → 필터링 -- 기존 알림 동작: 설정 미변경 시 기존과 동일하게 전체 상태에 대해 알림 - -## Progress Tracking -- Total Todos: 5 -- Completed: 5 -- Status: All todos completed - -## Change Log -- 2026-02-17: Plan created -- 2026-02-17: All todos executed and verified (build + tests pass) diff --git a/.claude/plan/realtime/2602/15-websocket-auto-refresh.plan.md b/.claude/plan/realtime/2602/15-websocket-auto-refresh.plan.md deleted file mode 100644 index 53ca3cf..0000000 --- a/.claude/plan/realtime/2602/15-websocket-auto-refresh.plan.md +++ /dev/null @@ -1,126 +0,0 @@ -# WebSocket 기반 칸반 보드 자동 새로고침 - -## Business Goal -외부 Hook API를 통해 작업이 생성/수정되면 칸반 보드가 실시간으로 반영되어야 한다. 또한 브라우저 뒤로가기로 보드에 돌아올 때 항상 최신 데이터를 로드해야 한다. - -## Scope -- **In Scope**: 보드 알림 WebSocket 채널, Hook API broadcast 연동, 클라이언트 자동 refresh 훅, 뒤로가기 시 자동 새로고침 -- **Out of Scope**: 실시간 부분 업데이트(전체 refresh 방식), 서버 액션 내부 변경 알림(이미 revalidatePath로 처리됨) - -## Codebase Analysis Summary -- 커스텀 서버(`server.ts`)에서 Next.js HTTP + 터미널 WebSocket 서버(port+10000) 운영 -- 터미널 WS는 `/api/terminal/:taskId` 경로만 처리 -- Hook API(`/api/hooks/start`, `/api/hooks/status`)는 DB 변경 후 `revalidatePath` 미호출 -- Board 컴포넌트는 `initialTasks` props를 `useState`로 관리, `useEffect`로 prop 변경 시 동기화 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/lib/boardNotifier.ts` | 보드 알림 broadcast 모듈 | Create | -| `src/hooks/useAutoRefresh.ts` | WebSocket 연결 + 뒤로가기 감지 훅 | Create | -| `server.ts` | 커스텀 서버 — WS 경로 추가 | Modify | -| `src/app/api/hooks/start/route.ts` | Hook API — broadcast 호출 추가 | Modify | -| `src/app/api/hooks/status/route.ts` | Hook API — broadcast 호출 추가 | Modify | -| `src/components/Board.tsx` | 보드 — 자동 refresh 훅 사용 | Modify | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| 한국어 주석 | CODE_PRINCIPLES.md | 모든 주석은 한국어 | -| 파일 배치 | project-architecture | 유틸은 `src/lib/`, 훅은 `src/hooks/` | -| 인증 패턴 | server.ts | `validateSessionFromCookie()` 사용 | -| "use client" | Next.js 컨벤션 | 클라이언트 훅 파일에 선언 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 알림 전달 방식 | 기존 WS 서버에 경로 추가 | `ws` 의존성 + 커스텀 서버 이미 존재 | SSE, Polling | -| 알림 메시지 | `{ type: "board-updated" }` 단순 신호 | `router.refresh()`로 전체 재로드하므로 payload 불필요 | Task diff 전송 | -| Broadcast 모듈 | `src/lib/boardNotifier.ts` 싱글턴 Set | API route에서 import 호출 | EventEmitter, Redis | -| 뒤로가기 감지 | `popstate` 이벤트 | 표준 브라우저 API, 안정적 | Performance Observer | - -## Implementation Todos - -### Todo 1: boardNotifier 모듈 생성 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: WebSocket 클라이언트 Set을 관리하고 broadcast 기능을 제공하는 모듈 생성 -- **Work**: - - `src/lib/boardNotifier.ts` 생성 - - `boardClients: Set` 전역 Set 관리 - - `addBoardClient(ws)`, `removeBoardClient(ws)`, `broadcastBoardUpdate()` 함수 export - - `broadcastBoardUpdate()`는 연결된 모든 클라이언트에 `{ type: "board-updated" }` JSON 전송 -- **Convention Notes**: 한국어 주석, 간결한 모듈 -- **Verification**: TypeScript 컴파일 확인 -- **Exit Criteria**: 모듈이 생성되고 3개 함수가 export됨 -- **Status**: completed - -### Todo 2: useAutoRefresh 커스텀 훅 생성 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: WebSocket 연결 + 뒤로가기 감지로 자동 새로고침하는 클라이언트 훅 -- **Work**: - - `src/hooks/useAutoRefresh.ts` 생성 (`"use client"`) - - WebSocket 연결: `ws://{host}:{wsPort}/api/board/events`로 연결 - - 메시지 수신 시 `router.refresh()` 호출 - - `popstate` 이벤트 감지로 뒤로가기 시 `router.refresh()` 호출 - - 자동 재연결 로직 (연결 끊김 시 3초 후 재시도) - - cleanup: unmount 시 WebSocket close + 이벤트 리스너 제거 -- **Convention Notes**: `"use client"` 선언, `useRouter`는 `@/i18n/navigation`에서 import -- **Verification**: TypeScript 컴파일 확인 -- **Exit Criteria**: 훅이 생성되고 WebSocket 연결 + popstate 감지 기능이 구현됨 -- **Status**: completed - -### Todo 3: server.ts에 보드 알림 WebSocket 경로 추가 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: 기존 WS 서버에서 `/api/board/events` 경로를 처리하여 boardNotifier에 클라이언트 등록 -- **Work**: - - `server.ts`의 `wss.on("connection")` 핸들러 수정 - - `/api/board/events` 경로 매칭 추가 (기존 터미널 경로 앞에) - - 인증 확인 (`validateSessionFromCookie`) - - 인증 성공 시 `addBoardClient(ws)`, close 시 `removeBoardClient(ws)` - - 기존 터미널 로직은 그대로 유지 -- **Convention Notes**: 기존 코드 패턴 유지, `boardNotifier` import 추가 -- **Verification**: 서버 빌드 확인 -- **Exit Criteria**: WS 서버가 두 경로(`/api/terminal/:id`, `/api/board/events`)를 모두 처리 -- **Status**: completed - -### Todo 4: Hook API routes에 broadcast 호출 추가 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: Hook API가 DB 변경 후 연결된 보드 클라이언트에 알림 전송 + revalidatePath 추가 -- **Work**: - - `src/app/api/hooks/start/route.ts`: 성공 응답 전 `broadcastBoardUpdate()` 호출 + `revalidatePath("/[locale]", "page")` 추가 - - `src/app/api/hooks/status/route.ts`: 성공 응답 전 `broadcastBoardUpdate()` 호출 + `revalidatePath("/[locale]", "page")` 추가 - - 두 파일에 `broadcastBoardUpdate` import 추가 -- **Convention Notes**: 기존 import 패턴 유지 -- **Verification**: TypeScript 컴파일 확인 -- **Exit Criteria**: 두 API route가 성공 시 broadcast + revalidatePath 호출 -- **Status**: completed - -### Todo 5: Board 컴포넌트에 useAutoRefresh 적용 -- **Priority**: 3 -- **Dependencies**: Todo 2 -- **Goal**: Board 컴포넌트에서 자동 새로고침 훅 활성화 -- **Work**: - - `src/components/Board.tsx`에 `useAutoRefresh` import 및 호출 - - Board 함수 최상단에 `useAutoRefresh()` 추가 -- **Convention Notes**: 최소한의 변경, 기존 코드에 영향 없음 -- **Verification**: 빌드 확인 -- **Exit Criteria**: Board 컴포넌트가 WebSocket 연결 + 뒤로가기 자동 새로고침 활성화 -- **Status**: completed - -## Verification Strategy -- TypeScript 빌드: `npx next build` (또는 `npx tsc --noEmit`) -- 수동 테스트: Hook API 호출 → 보드 자동 갱신 확인 -- 수동 테스트: 태스크 상세 → 뒤로가기 → 최신 데이터 확인 - -## Progress Tracking -- Total Todos: 5 -- Completed: 5 -- Status: Execution complete - -## Change Log -- 2026-02-15: Plan created -- 2026-02-15: All todos completed diff --git a/.claude/plan/settings/2602/22-default-session-type.plan.md b/.claude/plan/settings/2602/22-default-session-type.plan.md deleted file mode 100644 index ac7c42a..0000000 --- a/.claude/plan/settings/2602/22-default-session-type.plan.md +++ /dev/null @@ -1,117 +0,0 @@ -# 기본 세션 타입 설정 기능 - -## Business Goal -새 작업 생성 시 세션 타입이 항상 tmux로 고정되어 있어 zellij 사용자가 매번 수동으로 변경해야 한다. 설정에서 기본 세션 타입을 선택할 수 있게 하여 사용자 편의성을 높인다. - -## Scope -- **In Scope**: appSettings에 기본 세션 타입 설정 추가, ProjectSettings UI, CreateTaskModal/BranchTaskModal 기본값 반영, i18n 번역 -- **Out of Scope**: hooks API(`/api/hooks/start`)의 기본 세션 타입 (외부에서 직접 전달하므로 제어 불가) - -## Codebase Analysis Summary -- `appSettings.ts`에 KV 스토어 기반 설정 getter/setter 패턴이 존재 (`getAppSetting`/`setAppSetting`) -- `ProjectSettings.tsx`에 사이드바, 알림 등 설정 UI가 섹션별로 구성됨 -- `CreateTaskModal`의 세션 타입 `` 드롭다운으로 tmux/zellij 선택 가능하게 구현 - - `setDefaultSessionType()` 호출하여 변경 저장 -- **Convention Notes**: 기존 사이드바 접기 토글 UI 패턴 참고 -- **Verification**: UI 렌더링 확인 -- **Exit Criteria**: 설정에서 세션 타입 변경 시 DB에 저장 -- **Status**: pending - -### Todo 4: Board/page.tsx에서 기본 세션 타입 전달 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: 메인 페이지에서 기본 세션 타입을 로딩하여 Board → CreateTaskModal/BranchTaskModal로 전달 -- **Work**: - - `src/app/[locale]/page.tsx`에서 `getDefaultSessionType()` 호출 추가 - - `Board` props에 전달 - - `BoardProps`에 `defaultSessionType: SessionType` 추가 - - `Board`에서 `CreateTaskModal`, `BranchTaskModal`, `ProjectSettings`에 prop 전달 -- **Convention Notes**: 기존 `sidebarDefaultCollapsed` 전달 패턴 따름 -- **Verification**: TypeScript 컴파일 확인 -- **Exit Criteria**: 설정값이 모달까지 전달됨 -- **Status**: pending - -### Todo 5: CreateTaskModal/BranchTaskModal에서 기본값 반영 -- **Priority**: 3 -- **Dependencies**: Todo 4 -- **Goal**: 세션 타입 선택의 기본값을 설정에서 가져온 값으로 적용 -- **Work**: - - `CreateTaskModal`: props에 `defaultSessionType` 추가, `` 드롭다운 추가: "전체 프로젝트" + 개별 프로젝트 (isWorktree 제외) - - Column에 `filteredTasks[col.status]` 전달 - - CreateTaskModal에 `defaultProjectId={selectedProjectId}` 전달 -- **Convention Notes**: 기존 디자인 토큰 사용, useCallback/useMemo 패턴 유지 -- **Verification**: TypeScript 타입 에러 없음 -- **Exit Criteria**: 필터 선택 시 해당 프로젝트+worktree 태스크만 표시, localStorage 저장/복원 동작, 드래그 정상 동작 -- **Status**: pending - -### Todo 3: CreateTaskModal 기본 프로젝트 설정 -- **Priority**: 2 -- **Dependencies**: Todo 2 -- **Goal**: Board에서 전달된 defaultProjectId로 프로젝트를 자동 선택 -- **Work**: - - `CreateTaskModalProps`에 `defaultProjectId?: string` 추가 - - `selectedProjectId` useState 초기값을 `defaultProjectId || ""` 사용 - - 모달이 열릴 때(isOpen 변경 시) defaultProjectId로 리셋하는 useEffect 추가 -- **Convention Notes**: 기존 props 패턴 유지 -- **Verification**: TypeScript 타입 에러 없음 -- **Exit Criteria**: 필터 선택 후 "새 작업" 클릭 시 해당 프로젝트가 자동 선택됨 -- **Status**: pending - -## Verification Strategy -- TypeScript 타입 에러 없음 (`npx tsc --noEmit`) -- 3개 언어 JSON 파싱 오류 없음 - -## Progress Tracking -- Total Todos: 3 -- Completed: 0 -- Status: Planning complete - -## Change Log -- 2026-02-13: Plan created diff --git a/.claude/plan/ui/2602/13-kanban-project-name-tag.plan.md b/.claude/plan/ui/2602/13-kanban-project-name-tag.plan.md deleted file mode 100644 index a482d07..0000000 --- a/.claude/plan/ui/2602/13-kanban-project-name-tag.plan.md +++ /dev/null @@ -1,78 +0,0 @@ -# Kanban TaskCard에 프로젝트 이름 태그 표시 - -## Business Goal -칸반 보드의 TaskCard에 프로젝트 이름을 표시하여 어떤 프로젝트의 작업인지 한눈에 파악할 수 있게 한다. Worktree 프로젝트인 경우 메인 프로젝트 이름을 표시한다. - -## Scope -- **In Scope**: TaskCard에 프로젝트 이름 태그 추가, worktree→main project name 해석 로직, 프로젝트 태그 디자인 토큰, Board→Column→TaskCard props 전달 -- **Out of Scope**: 백엔드 쿼리 변경, 엔티티 수정, 프로젝트 정렬/필터링, Task Detail 페이지 변경 - -## Codebase Analysis Summary -Board 컴포넌트는 `projects: Project[]`를 이미 props로 받고 있으나 Column/TaskCard로 전달하지 않음. getTasksByStatus()는 project relation을 로드하지 않으며, TaskCard는 task.projectId만 접근 가능. 기존 태그 패턴은 `text-xs px-2 py-0.5 rounded-full bg-tag-*-bg text-tag-*-text` 형식. - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `prd/design-system.json` | 디자인 토큰 정의 | Modify | -| `src/app/globals.css` | CSS 변수 선언 + Tailwind 등록 | Modify | -| `src/components/Board.tsx` | 메인 보드 컨테이너 | Modify | -| `src/components/Column.tsx` | 상태별 컬럼 | Modify | -| `src/components/TaskCard.tsx` | 작업 카드 | Modify | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| 태그 스타일 | TaskCard.tsx | `text-xs px-2 py-0.5 rounded-full` 패턴 | -| 디자인 토큰 절차 | CLAUDE.md | design-system.json → globals.css :root → @theme inline | -| Props 타입 | Column.tsx, TaskCard.tsx | interface로 명시적 타입 정의 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 데이터 전달 | Board에서 projectNameMap 구성 후 props drilling | 기존 projects 데이터 활용, 백엔드 변경 없음 | getTasksByStatus에 relations 추가 | -| Worktree 해석 | repoPath에서 `__worktrees` 패턴 파싱 | 기존 디렉토리 컨벤션 활용 | isWorktree 플래그 + parentProjectId 컬럼 | -| 태그 색상 | yellow-50 bg + gray-800 text | 따뜻한 톤으로 시각적 구분, 기존 primitive 활용, 충분한 대비 | purple 계열 (비 Google 브랜드) | - -## Implementation Todos - -### Todo 1: 프로젝트 태그 디자인 토큰 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: `tag-project-bg/text` 디자인 토큰을 추가하여 프로젝트 태그에 사용할 색상을 정의한다 -- **Work**: - - `prd/design-system.json`의 `colors.tags` 객체에 `"project": { "background": "{yellow.50}", "text": "{gray.800}" }` 추가 - - `src/app/globals.css`의 `:root`에 `--color-tag-project-bg: var(--yellow-50)`, `--color-tag-project-text: var(--gray-800)` 추가 - - `src/app/globals.css`의 `@theme inline` 블록에 `--color-tag-project-bg`, `--color-tag-project-text` 등록 -- **Convention Notes**: 기존 tag 토큰 패턴(bg + text 쌍)과 동일한 구조 유지 -- **Verification**: CSS 파싱 오류 없는지 빌드 확인 -- **Exit Criteria**: `bg-tag-project-bg text-tag-project-text` Tailwind 클래스 사용 가능 -- **Status**: pending - -### Todo 2: Board → Column → TaskCard 프로젝트 이름 전달 및 표시 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: Board에서 projectNameMap을 구성하고 Column을 거쳐 TaskCard까지 전달, 카드에 프로젝트 이름 태그 렌더링 -- **Work**: - - `Board.tsx`: `projects` 배열로 `projectNameMap: Record` 생성 (`useMemo`) - - worktree 프로젝트(`repoPath`에 `__worktrees` 포함)는 메인 프로젝트 이름으로 resolve - - 메인 프로젝트 찾기: repoPath에서 `__worktrees` 이전 경로 추출 → 동일 repoPath의 프로젝트 name 사용, 없으면 경로의 basename 사용 - - `Column.tsx`: `ColumnProps`에 `projectNameMap: Record` 추가, TaskCard로 전달 - - `TaskCard.tsx`: `TaskCardProps`에 `projectName?: string` 추가, 브랜치 태그 앞에 프로젝트 이름 태그 렌더링 - - Board에서 Column 호출 시 `projectNameMap` 전달 - - Column에서 TaskCard 호출 시 `projectNameMap[task.projectId]`를 `projectName`으로 전달 -- **Convention Notes**: 기존 태그 스타일(`text-xs px-2 py-0.5 rounded-full`) 재사용, truncate로 긴 이름 처리 -- **Verification**: `npm run build` 성공, 타입 에러 없음 -- **Exit Criteria**: TaskCard에 프로젝트 이름 태그가 브랜치 태그 앞에 표시됨, worktree 프로젝트는 메인 프로젝트 이름 표시 -- **Status**: pending - -## Verification Strategy -- `npm run build` 성공 확인 -- TypeScript 타입 에러 없음 확인 - -## Progress Tracking -- Total Todos: 2 -- Completed: 0 -- Status: Planning complete - -## Change Log -- 2026-02-13: Plan created diff --git a/.claude/plan/ui/2602/14-fix-duplicated-branch-name.plan.md b/.claude/plan/ui/2602/14-fix-duplicated-branch-name.plan.md deleted file mode 100644 index b81b083..0000000 --- a/.claude/plan/ui/2602/14-fix-duplicated-branch-name.plan.md +++ /dev/null @@ -1,66 +0,0 @@ -# Fix Duplicated Branch Name Display - -## Business Goal -칸반 보드에서 브랜치 이름과 태스크 제목이 동일할 때 중복 표시되는 문제를 해결하여 UI의 가독성을 높인다. 카드에 description 미리보기(2줄)를 추가하여 태스크 내용을 한눈에 파악할 수 있게 한다. - -## Scope -- **In Scope**: TaskCard 브랜치 태그 조건부 숨김, 상세 페이지 브랜치 메타데이터 조건부 숨김, 카드 description 2줄 미리보기 -- **Out of Scope**: 데이터 모델 변경, CreateTaskModal 수정, 번역 파일 추가 - -## Codebase Analysis Summary -TaskCard.tsx에서 `task.title`과 `task.branchName`이 항상 동일 값(CreateTaskModal에서 branchName을 title로 설정)으로 카드에 제목+태그 두 번 표시. 상세 페이지(page.tsx)도 동일하게 제목 영역과 메타데이터 섹션에 중복. - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/components/TaskCard.tsx` | 칸반 카드 렌더링 | Modify | -| `src/app/[locale]/task/[id]/page.tsx` | 태스크 상세 페이지 | Modify | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| Tailwind CSS 변수 토큰 | CLAUDE.md | `text-text-secondary` 등 디자인 토큰 사용 | -| line-clamp | Tailwind v4 | `line-clamp-2` 유틸리티 클래스 사용 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 숨김 방식 | 조건부 (`branchName === title`) | title과 branchName이 다른 경우에도 올바르게 동작 | 항상 숨김 (유연성 부족) | -| Description 줄 제한 | CSS `line-clamp-2` | Tailwind 기본 지원, 추가 JS 불필요 | JS 기반 truncation | - -## Implementation Todos - -### Todo 1: TaskCard 브랜치 태그 조건부 숨김 + description 미리보기 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 카드에서 브랜치 태그 중복 제거, description 2줄 미리보기 추가 -- **Work**: - - `src/components/TaskCard.tsx` 47-51행: `task.branchName` 렌더링 조건에 `task.branchName !== task.title` 추가 - - `task.title` h3 태그 아래, 태그 영역 위에 `task.description`을 `line-clamp-2`로 표시하는 p 태그 추가 -- **Convention Notes**: 텍스트 색상은 `text-text-secondary`, 폰트 크기는 `text-xs` -- **Verification**: `npm run build`로 빌드 성공 확인 -- **Exit Criteria**: 카드에서 branchName === title일 때 브랜치 태그 미표시, description 2줄 표시 -- **Status**: pending - -### Todo 2: 상세 페이지 브랜치 메타데이터 조건부 숨김 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 상세 페이지에서 브랜치 메타데이터 중복 제거 -- **Work**: - - `src/app/[locale]/task/[id]/page.tsx` 129-138행: `task.branchName` 렌더링 조건에 `task.branchName !== task.title` 추가 -- **Convention Notes**: 기존 코드 패턴 유지, 조건만 추가 -- **Verification**: `npm run build`로 빌드 성공 확인 -- **Exit Criteria**: 상세 페이지에서 branchName === title일 때 브랜치 메타데이터 미표시 -- **Status**: pending - -## Verification Strategy -- `npm run build`로 전체 빌드 성공 확인 - -## Progress Tracking -- Total Todos: 2 -- Completed: 2 -- Status: Execution complete - -## Change Log -- 2026-02-14: Plan created -- 2026-02-14: All todos completed, TypeScript check passed diff --git a/.claude/plan/ui/2602/15-fzf-folder-search.plan.md b/.claude/plan/ui/2602/15-fzf-folder-search.plan.md deleted file mode 100644 index 7d2ba7e..0000000 --- a/.claude/plan/ui/2602/15-fzf-folder-search.plan.md +++ /dev/null @@ -1,153 +0,0 @@ -# fzf 스타일 폴더 검색 UI - -## Business Goal -스캔할 폴더 경로를 수동 입력하는 대신, fzf 스타일의 fuzzy finder UI를 제공하여 홈 디렉토리 하위의 git 저장소를 빠르게 검색하고 키보드로 선택할 수 있게 한다. 사용자 경험을 개선하여 경로 오타를 방지하고 탐색 효율을 높인다. - -## Scope -- **In Scope**: - - 홈 디렉토리(~) 하위 git repo 목록 조회 Server Action - - fzf 스타일 fuzzy finder 컴포넌트 (타이핑 필터, 키보드 내비게이션, Enter 선택) - - ProjectSettings의 scanPath 입력부를 fzf 컴포넌트로 교체 - - SSH 호스트 지원 (원격 git repo 목록 조회) - - i18n 번역 키 추가 (ko, en, zh) -- **Out of Scope**: - - 스캔 로직 자체 변경 - - 디렉토리 생성/삭제 기능 - - 일반 디렉토리 표시 (git repo만) - -## Codebase Analysis Summary -- `ProjectSettings.tsx`: 프로젝트 관리 패널. ``로 경로 수동 입력 → `scanAndRegisterProjects` 호출 -- `src/app/actions/project.ts`: Server Action 모음. `scanAndRegisterProjects`가 `scanGitRepos` 호출 -- `src/lib/gitOperations.ts`: `scanGitRepos(rootPath, sshHost?)` — `find` 명령으로 `.git` 디렉토리 탐색, SSH 지원 -- `execGit(command, sshHost?)` — 로컬/원격 명령 실행 추상화 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/app/actions/project.ts` | Server Action 모음 | Modify — `listGitRepos` 액션 추가 | -| `src/components/FolderSearchInput.tsx` | fzf 스타일 fuzzy finder 컴포넌트 | Create | -| `src/components/ProjectSettings.tsx` | 프로젝트 설정 패널 | Modify — FolderSearchInput으로 교체 | -| `src/lib/gitOperations.ts` | Git 관련 유틸리티 | Reference (scanGitRepos 재사용) | -| `messages/ko.json` | 한국어 번역 | Modify — 새 키 추가 | -| `messages/en.json` | 영어 번역 | Modify — 새 키 추가 | -| `messages/zh.json` | 중국어 번역 | Modify — 새 키 추가 | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| "use client" 지시어 | 기존 컴포넌트 | 클라이언트 컴포넌트에 필수 | -| useTranslations 훅 | i18n 패턴 | 모든 UI 텍스트는 번역 키 사용 | -| Tailwind 디자인 토큰 | globals.css | `bg-bg-*`, `text-text-*`, `border-border-*` 클래스 사용 | -| Server Action + "use server" | project.ts | 서버 액션 패턴 유지 | -| 한국어 주석 | CODE_PRINCIPLES.md | 주석은 한국어로 작성 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| git repo 목록 조회 | 기존 `scanGitRepos` 재사용 | DRY, 이미 SSH 지원 | 별도 find 명령 구현 | -| Fuzzy matching | 프론트엔드 JS 필터 | 즉각 반응, 서버 부하 없음 | fuse.js 라이브러리 | -| 컴포넌트 분리 | `FolderSearchInput` 별도 파일 | SRP, 재사용 가능성 | ProjectSettings 내 인라인 | -| 드롭다운 위치 | 입력창 아래 absolute | 기존 UI 레이아웃 호환 | Modal/Portal | - -## Implementation Todos - -### Todo 1: Server Action — listGitRepos 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 홈 디렉토리 하위 git repo 경로 목록을 반환하는 서버 액션 생성 -- **Work**: - - `src/app/actions/project.ts`에 `listGitRepos(sshHost?: string): Promise` 추가 - - 내부에서 `scanGitRepos("~", sshHost)` 호출하여 결과 반환 - - `"use server"` 디렉티브 확인 (이미 파일 상단에 있음) -- **Convention Notes**: 기존 Server Action 패턴과 동일하게 export async function -- **Verification**: Server Action이 정상적으로 git repo 경로 배열을 반환하는지 확인 -- **Exit Criteria**: `listGitRepos()` 호출 시 `~` 하위 git repo 경로 배열 반환 -- **Status**: pending - -### Todo 2: FolderSearchInput 컴포넌트 생성 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: fzf 스타일 fuzzy finder 입력 컴포넌트 구현 -- **Work**: - - `src/components/FolderSearchInput.tsx` 생성 (클라이언트 컴포넌트) - - Props: `{ onSelect: (path: string) => void; sshHost?: string; name: string; placeholder?: string }` - - 상태 관리: - - `query`: 검색 입력값 - - `repos`: 서버에서 가져온 전체 git repo 목록 - - `filteredRepos`: fuzzy 필터링된 결과 - - `selectedIndex`: 현재 선택된 항목 인덱스 - - `isOpen`: 드롭다운 표시 여부 - - `isLoading`: 로딩 상태 - - 마운트 시 (또는 input focus 시) `listGitRepos(sshHost)` 호출하여 repo 목록 캐싱 - - 입력값 변경 시 fuzzy matching으로 필터링: - - 쿼리 문자열을 소문자로 변환 - - 경로의 각 문자가 쿼리의 순서대로 포함되는지 확인 (fzf 스타일 subsequence matching) - - 매칭 점수 기반 정렬 (연속 매칭 우선, 경로 끝부분 매칭 우선) - - 키보드 이벤트 처리: - - `ArrowDown`: selectedIndex 증가 - - `ArrowUp`: selectedIndex 감소 - - `Enter`: 현재 선택 항목 확정 → `onSelect(path)` 호출, 드롭다운 닫기 - - `Escape`: 드롭다운 닫기 - - 드롭다운 UI: - - 입력창 하단에 absolute 위치 - - 최대 높이 제한 + 스크롤 - - 선택된 항목 하이라이트 (bg-brand-primary/10) - - 매칭된 문자 하이라이트 (text-brand-primary font-bold) - - 외부 클릭 시 드롭다운 닫기 (useRef + useEffect) - - 선택 완료 후 입력창에 선택된 경로 표시 - - hidden input으로 form에 값 전달 (`name` prop 활용) -- **Convention Notes**: - - "use client" 지시어 필수 - - Tailwind 디자인 토큰 사용 (bg-bg-page, text-text-primary, border-border-default 등) - - 한국어 주석 -- **Verification**: 컴포넌트 렌더링 확인, 키보드 내비게이션 동작 확인 -- **Exit Criteria**: fuzzy 검색 → 화살표 키 내비게이션 → Enter 선택이 모두 동작 -- **Status**: pending - -### Todo 3: ProjectSettings 컴포넌트에 FolderSearchInput 통합 -- **Priority**: 2 -- **Dependencies**: Todo 1, Todo 2 -- **Goal**: 기존 scanPath 텍스트 입력을 FolderSearchInput으로 교체 -- **Work**: - - `src/components/ProjectSettings.tsx`에서 `` 제거 - - `FolderSearchInput` import 및 사용 - - `sshHost` 값을 FolderSearchInput에 전달 (SSH select 변경 시 연동) - - SSH host select를 controlled component로 변경하여 `sshHost` 상태 관리 - - `handleScan` 함수는 기존대로 formData에서 scanPath 읽음 (hidden input으로 전달됨) -- **Convention Notes**: 기존 form action 패턴 유지 -- **Verification**: 설정 패널에서 폴더 검색 → 선택 → 스캔 전체 플로우 동작 확인 -- **Exit Criteria**: fzf로 폴더 선택 후 스캔 버튼 클릭 시 정상 등록 -- **Status**: pending - -### Todo 4: i18n 번역 키 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: FolderSearchInput 관련 UI 텍스트 번역 키 추가 -- **Work**: - - `messages/ko.json`의 `settings` 네임스페이스에 추가: - - `"searchReposPlaceholder"`: "폴더명을 입력하여 검색..." - - `"loadingRepos"`: "git 저장소 검색 중..." - - `"noReposFound"`: "일치하는 저장소가 없습니다." - - `"repoCount"`: "{count}개의 저장소" - - `messages/en.json`에 동일 키 영어 번역 추가 - - `messages/zh.json`에 동일 키 중국어 번역 추가 -- **Convention Notes**: 기존 번역 파일 구조와 동일하게 settings 네임스페이스 사용 -- **Verification**: 각 locale에서 번역 키가 정상 출력되는지 확인 -- **Exit Criteria**: ko, en, zh 세 언어 모두 번역 키 존재 -- **Status**: pending - -## Verification Strategy -- `npm run build`로 빌드 오류 없음 확인 -- 브라우저에서 설정 패널 열기 → FolderSearchInput에 문자 입력 → 드롭다운 표시 확인 -- 키보드 화살표 + Enter로 선택 가능한지 확인 -- 선택 후 "스캔 및 등록" 버튼으로 정상 스캔되는지 확인 -- SSH host 변경 시 원격 repo 목록으로 갱신되는지 확인 - -## Progress Tracking -- Total Todos: 4 -- Completed: 4 -- Status: Execution complete - -## Change Log -- 2026-02-15: Plan created -- 2026-02-15: All todos executed and verified (build pass) diff --git a/.claude/plan/ui/2602/16-fix-back-navigation-stale-cache.plan.md b/.claude/plan/ui/2602/16-fix-back-navigation-stale-cache.plan.md deleted file mode 100644 index 19ec927..0000000 --- a/.claude/plan/ui/2602/16-fix-back-navigation-stale-cache.plan.md +++ /dev/null @@ -1,64 +0,0 @@ -# Fix: 뒤로가기 시 칸반 보드 캐시 데이터 표시 문제 - -## Business Goal -칸반 보드에서 태스크 상세 페이지로 이동 후 뒤로가기하면 Next.js 라우터 캐시로 인해 최신 데이터가 아닌 이전 캐싱 데이터가 표시되는 문제를 수정한다. - -## Scope -- **In Scope**: `src/hooks/useAutoRefresh.ts` 수정 — 모듈 레벨 플래그로 재마운트 감지 후 `router.refresh()` 호출, 비동작 `popstate` 리스너 제거 -- **Out of Scope**: 서버 캐싱 설정, next.config.ts, 다른 컴포넌트 변경 - -## Codebase Analysis Summary -- `HomePage` (`src/app/[locale]/page.tsx`)는 `dynamic = "force-dynamic"`으로 서버 캐싱 비활성화됨 -- `Board` 컴포넌트가 `useAutoRefresh()` 훅을 호출하며, 이 훅에 WebSocket + popstate 리스너가 있음 -- `popstate` 리스너는 Board 언마운트 후 이벤트가 발생하므로 실제 동작하지 않음 -- Board는 `initialTasks` props를 `useState`로 관리하며, `useEffect`로 변경 감지하여 동기화 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/hooks/useAutoRefresh.ts` | 자동 갱신 훅 (WebSocket + popstate) | Modify | -| `src/components/Board.tsx` | 칸반 보드 메인 컴포넌트 | Reference | -| `src/app/[locale]/page.tsx` | 홈페이지 서버 컴포넌트 | Reference | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| 주석 언어 | CODE_PRINCIPLES.md | 한국어로 작성 | -| 함수 단일 책임 | CODE_PRINCIPLES.md | 하나의 함수는 한 가지 일만 수행 | -| YAGNI | CODE_PRINCIPLES.md | 현재 필요한 것만 구현 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 재마운트 감지 방식 | 모듈 레벨 플래그 | 간단하고 확실, SPA 내비게이션에서 동작 | pageshow 이벤트(bfcache만), layout 레벨 리스너(과도한 변경) | -| popstate 리스너 | 제거 | Board 언마운트 시 해제되어 타이밍 문제로 동작 불가 | 유지(무해하지만 dead code) | - -## Implementation Todos - -### Todo 1: useAutoRefresh 훅 수정 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 뒤로가기로 Board가 재마운트될 때 `router.refresh()`를 호출하여 최신 데이터를 로드한다 -- **Work**: - - `src/hooks/useAutoRefresh.ts`에 모듈 레벨 변수 `boardHasMountedBefore` 추가 (초기값 `false`) - - 마운트 시 `boardHasMountedBefore`가 `true`이면 `router.refresh()` 호출 - - 마운트 후 `boardHasMountedBefore = true` 설정 - - 기존 `popstate` useEffect 블록 제거 (동작하지 않는 dead code) - - 기존 WebSocket useEffect 블록은 유지 -- **Convention Notes**: 주석은 한국어, JSDoc 스타일 준수 -- **Verification**: `pnpm build` 성공 확인 -- **Exit Criteria**: `useAutoRefresh.ts`에 모듈 레벨 플래그 기반 refresh 로직이 존재하고, popstate 리스너가 제거됨 -- **Status**: completed - -## Verification Strategy -- `pnpm build` 성공 -- 코드 리뷰: 모듈 레벨 변수와 useEffect 로직 확인 - -## Progress Tracking -- Total Todos: 1 -- Completed: 1 -- Status: Execution complete - -## Change Log -- 2026-02-16: Plan created -- 2026-02-16: Todo 1 completed — useAutoRefresh 훅 수정 diff --git a/.claude/plan/ui/2602/16-header-logo-favicon.plan.md b/.claude/plan/ui/2602/16-header-logo-favicon.plan.md deleted file mode 100644 index 92c2b39..0000000 --- a/.claude/plan/ui/2602/16-header-logo-favicon.plan.md +++ /dev/null @@ -1,65 +0,0 @@ -# Header Logo & Favicon Setup - -## Business Goal -KanVibe 브랜드 아이덴티티 강화를 위해 헤더에 로고 이미지를 표시하고, 웹사이트 파비콘을 커스텀 로고로 설정한다. - -## Scope -- **In Scope**: 헤더 로고 이미지 배치, 파비콘 교체, 기존 favicon.ico 제거 -- **Out of Scope**: 다크모드 대응, apple-touch-icon, 반응형 로고 크기 변경 - -## Codebase Analysis Summary -- 헤더는 `src/components/Board.tsx`의 `
` 태그 내에 `

` 텍스트로 구현 -- 파비콘은 `src/app/favicon.ico`에 기본 Next.js 아이콘 사용 중 -- 이미지 파일은 프로젝트 루트에 `kanvibe-left-header.png`, `kanvibe-logo.png` 존재 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `kanvibe-left-header.png` | 헤더 로고 원본 | Move to `public/` | -| `kanvibe-logo.png` | 파비콘 원본 | Move to `src/app/icon.png` | -| `src/components/Board.tsx` | 메인 보드 헤더 | Modify (h1 → Image) | -| `src/app/favicon.ico` | 기존 파비콘 | Delete | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| Next.js Image | Framework | `next/image` 컴포넌트 사용 | -| File-based metadata | Next.js App Router | `src/app/icon.png` 컨벤션으로 파비콘 자동 인식 | - -## Implementation Todos - -### Todo 1: Move image files to correct locations -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 이미지 파일을 Next.js가 인식할 수 있는 위치로 이동 -- **Work**: - - `kanvibe-left-header.png` → `public/kanvibe-left-header.png` 복사 - - `kanvibe-logo.png` → `src/app/icon.png` 복사 - - `src/app/favicon.ico` 삭제 -- **Convention Notes**: Next.js App Router file convention (`icon.png` in app dir) -- **Verification**: 파일 존재 확인 -- **Exit Criteria**: `public/kanvibe-left-header.png`, `src/app/icon.png` 존재, `favicon.ico` 제거 -- **Status**: pending - -### Todo 2: Replace header text with logo image -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: Board 헤더의 텍스트를 이미지 로고로 교체 -- **Work**: - - `src/components/Board.tsx` line 345의 `

` 태그를 `next/image` `` 컴포넌트로 교체 - - `src="/kanvibe-left-header.png"`, 적절한 height/width, alt text 설정 -- **Convention Notes**: Next.js `` 컴포넌트 사용, alt 텍스트 필수 -- **Verification**: `npm run build` 성공 -- **Exit Criteria**: 헤더에 로고 이미지가 표시되고 빌드 에러 없음 -- **Status**: pending - -## Verification Strategy -- `npm run build` 성공 확인 - -## Progress Tracking -- Total Todos: 2 -- Completed: 2 -- Status: Execution complete - -## Change Log -- 2026-02-16: Plan created diff --git a/.claude/plan/ui/2602/16-project-selector-single-dropdown.plan.md b/.claude/plan/ui/2602/16-project-selector-single-dropdown.plan.md deleted file mode 100644 index 34b8e12..0000000 --- a/.claude/plan/ui/2602/16-project-selector-single-dropdown.plan.md +++ /dev/null @@ -1,66 +0,0 @@ -# ProjectSelector 단일 선택 모드 드롭다운 UI 변경 - -## Business Goal -새 작업 생성 모달에서 프로젝트 선택 시, 기존 input 기반 UI가 사용하기 불편하므로 검색 필터의 멀티 선택 드롭다운 스타일과 동일한 UX로 변경하여 프로젝트 선택 편의성을 향상시킨다. - -## Scope -- **In Scope**: `ProjectSelector.tsx`의 단일 선택 모드(line 333~461) 렌더링 변경 -- **Out of Scope**: 멀티 선택 모드 변경 없음, CreateTaskModal 로직 변경 없음, Board.tsx 변경 없음 - -## Codebase Analysis Summary -`ProjectSelector`는 discriminated union props로 single/multi 모드를 지원한다. -- 멀티 모드: 클릭 트리거(칩 표시) + 드롭다운(검색 input + 체크박스 리스트) -- 단일 모드: input 자체가 트리거 겸 검색 → 포커스 시 기존 프로젝트명 pre-fill, 혼란 유발 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/components/ProjectSelector.tsx` | 프로젝트 선택 컴포넌트 | Modify | -| `src/components/CreateTaskModal.tsx` | 새 작업 생성 모달 (단일 선택 사용처) | Reference | -| `src/components/Board.tsx` | 칸반 보드 (멀티 선택 사용처) | Reference | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| Tailwind CSS 변수 토큰 | CLAUDE.md | `bg-bg-page`, `text-text-primary` 등 사용 | -| 한국어 주석 | CODE_PRINCIPLES.md | 주석은 한국어로 작성 | -| 기존 멀티 선택 스타일 | ProjectSelector.tsx line 205~330 | 동일한 className 패턴 사용 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 트리거 UI | 클릭 트리거 div + 선택된 프로젝트 텍스트 표시 | 멀티 모드와 일관된 UX | 기존 input 유지 | -| 검색 위치 | 드롭다운 내부 검색 input | 멀티 모드와 동일한 패턴 | 트리거 자체를 검색으로 유지 | -| 선택 시 동작 | 즉시 선택 + 드롭다운 닫힘 + 검색 초기화 | 단일 선택이므로 선택 완료 시 닫는 것이 자연스러움 | 드롭다운 유지 | - -## Implementation Todos - -### Todo 1: 단일 선택 모드 렌더링을 드롭다운 스타일로 변경 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 단일 선택 모드의 UI를 멀티 선택 드롭다운 스타일(클릭 트리거 + 드롭다운 내부 검색)로 변경 -- **Work**: - - `ProjectSelector.tsx`의 단일 선택 렌더링(line 387~461)을 멀티 선택 렌더링(line 205~330) 스타일로 교체 - - 트리거 div: 선택된 프로젝트명을 텍스트로 표시 (칩 스타일 없이 단순 텍스트), 없으면 placeholder - - 드롭다운: 검색 input(상단) + 프로젝트 리스트(체크박스 없이, 선택된 항목은 `font-medium`으로 강조) - - `allOption`이 있는 경우 드롭다운 상단에 "전체" 옵션 유지 - - 포커스 자동 이동: 드롭다운 열릴 때 `searchInputRef`에 포커스 - - 키보드 네비게이션: 기존 `handleSingleKeyDown` 로직 유지 (ArrowDown/Up, Enter, Escape) - - `triggerInputRef` 제거 (더 이상 input이 트리거가 아님) -- **Convention Notes**: 멀티 선택 모드의 className 패턴 그대로 재사용 (compact 지원 포함) -- **Verification**: 빌드 성공 (`npm run build`), CreateTaskModal에서 프로젝트 선택/검색/변경 동작 확인 -- **Exit Criteria**: 단일 선택 모드가 드롭다운 스타일로 동작하며, 프로젝트 검색/선택/키보드 네비게이션이 정상 동작 -- **Status**: completed - -## Verification Strategy -- `npm run build` 성공 -- CreateTaskModal에서 프로젝트 선택, 검색, 변경 동작 확인 -- Board.tsx의 멀티 선택 필터가 영향받지 않음 확인 - -## Progress Tracking -- Total Todos: 1 -- Completed: 1 -- Status: Execution complete - -## Change Log -- 2026-02-16: Plan created diff --git a/.claude/plan/ui/2602/17-done-status-alert.plan.md b/.claude/plan/ui/2602/17-done-status-alert.plan.md deleted file mode 100644 index f417112..0000000 --- a/.claude/plan/ui/2602/17-done-status-alert.plan.md +++ /dev/null @@ -1,96 +0,0 @@ -# Done 상태 이동 시 리소스 삭제 경고 - -## Business Goal -태스크를 Done으로 이동하면 worktree, branch, tmux session이 삭제되는데, 사용자가 이를 인지하지 못하고 실수로 리소스를 잃는 것을 방지하기 위해 확인 다이얼로그를 표시한다. - -## Scope -- **In Scope**: 칸반 D&D와 상세 페이지에서 Done 이동 시 confirm 경고 추가, 3개 언어 번역 -- **Out of Scope**: 커스텀 모달 UI, 다른 상태 전환 경고 - -## Codebase Analysis Summary - -Done 상태 전환 시 `cleanupTaskResources()`가 호출되어 session(tmux/zellij), worktree, branch를 삭제한다. -두 경로 존재: Board.tsx `handleDragEnd` → `moveTaskToColumn`, 상세 페이지 `handleStatusChange` → `updateTaskStatus`. -기존 삭제 경고는 `confirm()` 네이티브 다이얼로그 사용 (`DeleteTaskButton`, `handleDeleteFromCard`). - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/components/Board.tsx` | 칸반 보드 D&D 핸들러 | Modify | -| `src/app/[locale]/task/[id]/page.tsx` | 상세 페이지 상태 변경 | Modify | -| `src/components/DoneStatusButton.tsx` | Done 버튼 클라이언트 래퍼 (신규) | Create | -| `messages/ko.json` | 한국어 번역 | Modify | -| `messages/en.json` | 영어 번역 | Modify | -| `messages/zh.json` | 중국어 번역 | Modify | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| 확인 다이얼로그 | `DeleteTaskButton.tsx` | `confirm()` 네이티브 사용 | -| i18n 키 추가 | `messages/*.json` | 3개 언어 동시 추가 | -| 클라이언트 컴포넌트 | `DeleteTaskButton.tsx` | `"use client"` + `useTranslations` | -| 한국어 주석 | CODE_PRINCIPLES.md | 주석은 한국어 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 다이얼로그 타입 | `confirm()` | 기존 삭제 패턴 동일 | 커스텀 모달 | -| 상세 페이지 구현 | `DoneStatusButton` 클라이언트 컴포넌트 | `DeleteTaskButton` 패턴과 동일 | 페이지 전체 client 전환 | -| 경고 조건 | 리소스(branch/session) 존재 시만 | 불필요한 UX 방해 방지 | 항상 경고 | - -## Implementation Todos - -### Todo 1: 번역 키 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: Done 이동 경고 메시지 번역 키를 3개 언어에 추가 -- **Work**: - - `messages/ko.json`의 `board` 네임스페이스에 `doneConfirm` 키 추가 - - `messages/ko.json`의 `taskDetail` 네임스페이스에 `doneConfirm` 키 추가 - - `messages/en.json`, `messages/zh.json`에 동일 키 추가 - - 메시지 내용: "Done으로 이동하면 worktree, branch, tmux session이 삭제됩니다. 계속하시겠습니까?" -- **Convention Notes**: 3개 언어 동시 수정 -- **Verification**: JSON 파싱 오류 없음 확인 -- **Exit Criteria**: 3개 언어 파일에 `doneConfirm` 키 존재 -- **Status**: pending - -### Todo 2: Board.tsx D&D 경고 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 칸반 드래그 앤 드롭으로 Done 이동 시 confirm 표시 -- **Work**: - - `Board.tsx`의 `handleDragEnd`에서 `destStatus === TaskStatus.DONE`이고 태스크에 리소스(branchName 또는 sessionType)가 있을 때 `confirm()` 호출 - - 취소 시 `return` (카드가 원래 위치로 복귀) - - 번역은 `useTranslations("board")`의 `doneConfirm` 사용 -- **Convention Notes**: 기존 `handleDragEnd` 구조 유지, 최소 변경 -- **Verification**: 빌드 성공 -- **Exit Criteria**: D&D로 Done 이동 시 리소스 있는 태스크에 confirm 표시 -- **Status**: pending - -### Todo 3: DoneStatusButton 컴포넌트 생성 + 상세 페이지 적용 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 상세 페이지 Done 버튼에 confirm 경고 추가 -- **Work**: - - `src/components/DoneStatusButton.tsx` 생성 (클라이언트 컴포넌트) - - `DeleteTaskButton` 패턴 참고: props로 `statusChangeAction`, `label`, `hasResources` 수신 - - `hasResources`가 true일 때만 `onSubmit`에서 `confirm()` 호출 - - `src/app/[locale]/task/[id]/page.tsx`에서 Done 전환 버튼만 `DoneStatusButton`으로 교체 -- **Convention Notes**: `"use client"`, `useTranslations("taskDetail")` 패턴 준수 -- **Verification**: 빌드 성공 -- **Exit Criteria**: 상세 페이지 Done 버튼 클릭 시 리소스 있으면 confirm 표시 -- **Status**: pending - -## Verification Strategy -- `pnpm build` 성공 -- 3개 언어 JSON 유효성 -- Board D&D / 상세 페이지 양쪽 경로에서 경고 동작 확인 - -## Progress Tracking -- Total Todos: 3 -- Completed: 3 -- Status: Execution complete - -## Change Log -- 2026-02-17: Plan created -- 2026-02-17: All todos completed, TypeScript + JSON validation passed diff --git a/.claude/plan/ui/2602/17-hooks-status-dialog.plan.md b/.claude/plan/ui/2602/17-hooks-status-dialog.plan.md deleted file mode 100644 index d582272..0000000 --- a/.claude/plan/ui/2602/17-hooks-status-dialog.plan.md +++ /dev/null @@ -1,161 +0,0 @@ -# 플랜: Task 상세 페이지 Hooks Status Dialog 구현 - -## 비즈니스 목표 - -Task 상세 페이지에서 Hooks 상태를 Dialog로 표시하여 사용자 경험을 개선하고, ProjectSettings 목록에는 신호등 표시기만 노출하여 UI 복잡도를 낮춘다. - -## 구현 세부사항 - -### 대상 파일 - -1. **src/components/ProjectSettings.tsx** - 신호등 표시기 추가 -2. **src/app/[locale]/task/[id]/page.tsx** - Dialog 오픈 버튼 추가 -3. **src/components/HooksStatusDialog.tsx** - 새 Dialog 컴포넌트 (생성) -4. **src/hooks/useHooksStatus.ts** - Custom hook (생성, 선택사항) - -### 신호등 상태 로직 - -```typescript -// 상태 계산 로직 -- 모두 설치 (Claude, Gemini, Codex 모두 Installed) → 🟢 "All OK" -- 일부 설치 → 🟡 "Partial" -- 미설치 (모두 미설치) → 🔴 "Not Installed" -``` - -### HooksStatusDialog 구조 - -```tsx -interface HooksStatusDialogProps { - isOpen: boolean; - onClose: () => void; - projectId: string; - isPending?: boolean; -} - -// 렌더링 - -

Hooks Status

- - {/* Claude Hooks */} -
- Claude - {status.installed ? "Installed" : "Not Installed"} - or -
- - {/* Gemini Hooks */} - {/* Codex Hooks */} - - -
-``` - -## 컨벤션 준수 - -- **명명 규칙**: `HooksStatusDialog` (기존 Dialog 패턴 따름) -- **상태 관리**: useState + useTransition (기존 패턴) -- **i18n**: useTranslations("task") 또는 새 namespace -- **스타일**: Tailwind CSS v4 + 기존 디자인 토큰 사용 -- **Custom Hook**: useHooksStatus (선택사항 - 상태 로직 분리 시) - -## 단계별 Todos - -### Todo 1: useHooksStatus Custom Hook 생성 (우선도 1, 선택사항) -- **목표**: 신호등 상태 계산 로직을 재사용 가능한 hook으로 분리 -- **상세 작업**: - - `src/hooks/useHooksStatus.ts` 생성 - - projectId → hooks status 조회 로직 구현 - - 상태 계산: (installed count) 기반 신호등 결정 - - Return type: `{ claudeStatus, geminiStatus, codexStatus, overallStatus }` -- **검증 기준**: 모든 조합의 상태 계산 정확도 확인 -- **종료 기준**: Hook이 정상 작동하고 재사용 가능 - -### Todo 2: HooksStatusDialog 컴포넌트 생성 (우선도 1) -- **목표**: Hooks 상태를 표시하는 Dialog 컴포넌트 구현 -- **상세 작업**: - - `src/components/HooksStatusDialog.tsx` 생성 - - Props: isOpen, onClose, projectId, isPending - - 내부 로직: - - useTransition으로 설치 상태 관리 - - getProjectHooksStatus, getProjectGeminiHooksStatus, getProjectCodexHooksStatus 호출 - - 각 hooks별 설치/재설치 버튼 렌더링 - - 스타일: 기존 Dialog 패턴 (DoneConfirmDialog 참고) -- **검증 기준**: - - Dialog 열기/닫기 정상 작동 - - 각 hooks 상태 표시 정확 - - 설치 버튼 클릭 시 action 실행 -- **종료 기준**: Dialog 렌더링 및 상호작용 완벽 - -### Todo 3: Task 상세 페이지에서 Dialog 오픈 버튼 추가 (우선도 2, Todo 2 의존) -- **목표**: Task 상세 페이지 기존 "Hooks Status" 섹션을 Dialog 오픈 버튼으로 대체 -- **상세 작업**: - - `src/app/[locale]/task/[id]/page.tsx` 수정 - - 기존 hooks status 섹션 제거 - - Dialog 오픈 버튼 추가 (신호등 + 텍스트) - - HooksStatusDialog import 및 state 관리 - - 신호등 상태 계산 (useHooksStatus 또는 inline) -- **검증 기준**: - - 버튼 클릭 시 Dialog 열림 - - 신호등 표시 정확 -- **종료 기준**: Task 상세 페이지에서 Dialog 정상 작동 - -### Todo 4: ProjectSettings 목록에 신호등 추가 (우선도 2) -- **목표**: 각 프로젝트의 신호등 상태 표시기 추가 -- **상세 작업**: - - `src/components/ProjectSettings.tsx` 수정 - - 프로젝트별 hooks 상태 계산 - - Delete 버튼 우측에 신호등 아이콘 + 상태명 표시 - - 신호등 색상: 🟢 (All OK) / 🟡 (Partial) / 🔴 (Not Installed) -- **검증 기준**: - - 신호등 표시 정확도 - - UI 레이아웃 깔끔함 -- **종료 기준**: 모든 프로젝트에 신호등 표시됨 - -### Todo 5: i18n 번역 추가 (우선도 3, Todo 2-3 의존) -- **목표**: Dialog 및 상태 텍스트 다국어 지원 -- **상세 작업**: - - `messages/ko.json`, `messages/en.json`, `messages/zh.json`에 필요한 키 추가 - - Keys: "hooksStatus", "hooksInstalled", "notInstalled", "allOk", "partial", etc. -- **검증 기준**: 모든 언어 번역 완료 및 정확도 -- **종료 기준**: 다국어 지원 정상 작동 - -### Todo 6: 스타일링 및 디자인 토큰 적용 (우선도 3, Todo 2-4 의존) -- **목표**: Dialog 및 신호등 UI를 디자인 시스템에 맞게 구성 -- **상세 작업**: - - Dialog: 기존 패턴 (bg-bg-surface, border-border-default, etc.) 적용 - - 신호등: 상태별 색상 토큰 사용 - - All OK: `text-status-done` or `text-brand-primary` - - Partial: `text-status-review` or `text-yellow-500` - - Not Installed: `text-status-error` -- **검증 기준**: 디자인 일관성 -- **종료 기준**: UI가 디자인 시스템과 일치 - -## 검증 전략 - -### 스태틱 검증 -1. TypeScript 컴파일 성공 (`pnpm check`) -2. ESLint 통과 (`pnpm lint`) - -### 동적 검증 -1. 개발 서버 시작 (`pnpm dev`) -2. Task 상세 페이지에서: - - Dialog 오픈 버튼 표시 ✓ - - 신호등 표시 (상태별 정확도) ✓ - - Dialog 열기 ✓ - - Dialog에서 hooks 상태 표시 ✓ - - 설치/재설치 버튼 작동 ✓ -3. ProjectSettings 목록에서: - - 신호등 표시 ✓ - - 상태명 표시 ✓ - -## 컨벤션 준수 노트 - -- **KISS**: 필요한 최소 기능만 구현 -- **네이밍**: 기존 Dialog 컴포넌트 패턴 따름 (DoneConfirmDialog, CreateTaskModal 참고) -- **스타일**: Tailwind CSS v4 + 디자인 시스템 토큰 사용 -- **상태 관리**: useState + useTransition (기존 패턴) -- **다국어**: next-intl 활용 - -## 오픈 질문 - -없음 - 요구사항 명확함 diff --git a/.claude/plan/ui/2602/17-project-branch-task-navigation.plan.md b/.claude/plan/ui/2602/17-project-branch-task-navigation.plan.md deleted file mode 100644 index 64aaf1d..0000000 --- a/.claude/plan/ui/2602/17-project-branch-task-navigation.plan.md +++ /dev/null @@ -1,231 +0,0 @@ -# 프로젝트 이름 색상 변경 및 브랜치 작업 네비게이션 - -## Business Goal -Task Detail 페이지에서 프로젝트 이름을 더 진하고 가시성 높은 색상(#202632)으로 표시하고, 브랜치 이름을 클릭할 수 있게 만들어 같은 프로젝트의 다른 작업들을 빠르게 탐색할 수 있도록 개선한다. - -## Scope -- **In Scope**: - - 설계 시스템 토큰에 새로운 프로젝트 배경색 추가 (#202632) - - Task Detail 페이지 메타데이터에 브랜치 이름 섹션 추가 - - 브랜치 이름 옆에 오른쪽 화살표(→) 아이콘 버튼 추가 - - 클릭 시 같은 프로젝트의 모든 작업을 브랜치별로 그룹화한 칸반 다이얼로그 표시 - - 다이얼로그에서 브랜치/작업 선택 후 해당 작업으로 이동 - -- **Out of Scope**: - - 기존 브랜치 이름 태그의 스타일 변경 (현재 gray.100 유지) - - 보드 페이지의 TaskCard 컴포넌트 수정 - - 새로운 API 엔드포인트 추가 - -## Codebase Analysis Summary - -### 설계 시스템 -- 토큰: `prd/design-system.json`에서 정의, `src/app/globals.css`의 CSS 변수로 구현 -- 프로젝트 태그: 현재 `yellow.50` 배경(`#FEF7E0`), `gray.800` 텍스트(`#424242`) -- 새로운 색상 #202632는 text.primary와 유사한 진한 회색 - -### Task Detail Page -- 파일: `src/app/[locale]/task/[id]/page.tsx` (서버 컴포넌트) -- 메타데이터 섹션: line 126-206, `
` 구조로 정보 표시 -- 현재 표시 정보: project, priority, prUrl, agentType, sessionType, sshHost, createdAt, updatedAt -- `task.branchName` 필드 존재하지만 아직 UI에 표시되지 않음 - -### 기존 Dialog 패턴 -- BranchTaskModal: 브랜치 분기 모달 사용 (`src/components/BranchTaskModal.tsx`) -- 고정 오버레이(`z-[400]`), 중앙 정렬, shadow/border 스타일 -- form 기반으로 구현되어 있음 - -### Task 데이터 구조 -- `KanbanTask` 엔티티: `branchName`, `projectId`, `baseBranch`, `project` (관계) 필드 -- Board에서 같은 프로젝트의 작업들을 필터링하는 로직 이미 존재 (projectNameMap, projectFilterSet) - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `prd/design-system.json` | 설계 토큰 정의 | Modify - 프로젝트 배경색 토큰 추가 | -| `src/app/globals.css` | CSS 변수 선언 | Modify - 새 색상 변수 추가 | -| `src/app/[locale]/task/[id]/page.tsx` | Task Detail 페이지 | Modify - 메타데이터에 브랜치 섹션 추가, 모달 상태 관리 추가 | -| `src/components/ProjectBranchTasksModal.tsx` | 새 모달 컴포넌트 | Create - 같은 프로젝트 작업 브랜치별 표시 | -| `src/app/actions/kanban.ts` | 작업 조회 로직 | Reference - getTasksByProjectId 함수 확인 | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| 색상 토큰 네이밍 | design-system.json | `tags.project.background` 형식으로 정의 | -| 컴포넌트 파일명 | 기존 컴포넌트들 | PascalCase, .tsx 확장자 | -| 클라이언트 컴포넌트 | 기존 모달들 | `"use client"` 지시문 필수 | -| CSS 클래스 | tailwind + design-system | `bg-tag-project-bg` 같은 시맨틱 클래스 사용 | -| 주석 | CODE_PRINCIPLES.md | 한국어로 작성, JSDoc 형식 | -| 상태 관리 | Task Detail | useState/useTransition으로 모달 열기/닫기 | -| i18n | next-intl | `useTranslations()` 훅 사용 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 색상 저장 방식 | 설계 토큰 추가 후 Tailwind arbitrary로 적용 | 일관성 유지, 다른 토큰과 동일 방식 | 직접 hex 색상 사용 (권장 안 함) | -| 모달 컴포넌트 위치 | `src/components/ProjectBranchTasksModal.tsx` | 기존 모달들과 같은 위치 | ProjectBranchTasksDialog.tsx (naming) | -| 모달 표시 방식 | 고정 오버레이 + 중앙 정렬 | 기존 BranchTaskModal과 동일 패턴 | Drawer/Sidebar (다른 UX) | -| 브랜치별 그룹화 | Map 구조 | 프론트엔드에서 처리, 간단하고 효율적 | 백엔드 정렬 (불필요한 복잡성) | -| 작업 조회 | 클라이언트에서 same projectId 필터링 | 기존 Board 로직과 동일 패턴 | API 엔드포인트 추가 (과설계) | - -## Implementation Todos - -### Todo 1: 설계 시스템 토큰 및 CSS 변수 업데이트 -- **Priority**: 1 (독립적) -- **Dependencies**: none -- **Goal**: 프로젝트 배경색을 #202632로 변경하여 설계 시스템에 반영 -- **Work**: - - `prd/design-system.json` 열기 - - `colors.tags.project` 객체의 `background` 값을 `"#202632"`로 변경 (현재: `"{yellow.50}"`) - - `src/app/globals.css`의 `:root` 섹션에서 `--color-tag-project-bg: var(--color-yellow-50);` 줄을 `--color-tag-project-bg: #202632;`로 변경 - - Tailwind @theme inline 블록 확인 (자동 생성되므로 수정 불필요) -- **Convention Notes**: - - design-system.json은 시맨틱 참조 형식 지원 → primitive 색상 참조 가능하지만 직접 hex도 가능 - - CSS 변수명은 기존 규칙 유지: `--color-tag-project-bg` -- **Verification**: - - design-system.json 문법 검증 (JSON 유효성) - - globals.css 문법 검증 (CSS 유효성) - - 빌드 성공 확인: `pnpm build` - - 색상 변경 시각 확인 (브라우저에서 프로젝트 태그 색상이 #202632로 표시되는지) -- **Exit Criteria**: - - 파일 수정 완료 - - 빌드 성공 - - task detail 페이지 프로젝트 태그가 어두운 회색(#202632) 배경으로 표시됨 - -### Todo 2: ProjectBranchTasksModal 컴포넌트 생성 -- **Priority**: 1 (독립적) -- **Dependencies**: none -- **Goal**: 같은 프로젝트의 모든 작업을 브랜치별로 그룹화하여 표시하는 모달 컴포넌트 구현 -- **Work**: - - 새 파일 `src/components/ProjectBranchTasksModal.tsx` 생성 - - `"use client"` 지시문 추가 - - Props 타입: `{ projectId: string; currentBranchName: string | null; tasks: KanbanTask[]; onClose: () => void; onSelectTask: (taskId: string) => void; }` - - 모달 구조: - - 고정 오버레이: `z-[400]`, `bg-bg-overlay`, 클릭 시 닫기 - - 모달 본체: `max-w-2xl`, `bg-bg-surface`, `rounded-xl`, `border-border-default`, `shadow-lg` - - 헤더: 제목 "프로젝트 작업 목록" (i18n), 닫기 버튼 - - 본문: 브랜치별 섹션으로 그룹화 - - `Map` 구조로 그룹화 - - 각 브랜치마다 `
` 또는 `
` 섹션 - - 브랜치명 표시: `` - - 작업 목록: TaskCard 컴포넌트 재사용 또는 간단한 리스트 (링크 클릭 시 `onSelectTask(taskId)` 호출) - - 국제화: `useTranslations("projectBranchTasks")` 사용 - - 스타일: 기존 BranchTaskModal 스타일과 일관성 -- **Convention Notes**: - - 컴포넌트 네이밍: `ProjectBranchTasksModal` (모달 타입 명시) - - Props는 명확하게 named parameters - - 브랜치별 그룹화는 프론트엔드에서 수행 (단순성) - - TaskCard 재사용이 과할 수 있으니, 간단한 리스트 구조 추천 (projectName 표시 불필요) -- **Verification**: - - TypeScript 컴파일 성공 - - 모달이 열릴 때 정상 렌더링 (console 에러 없음) - - 같은 프로젝트의 작업들이 브랜치별로 그룹화되어 표시됨 - - 작업 클릭 시 `onSelectTask` 호출 확인 (개발자 도구) -- **Exit Criteria**: - - 파일 생성 완료 - - 컴포넌트 컴파일 성공 - - 모달 구조 시각 확인 (브라우저에서) - -### Todo 3: Task Detail 페이지 메타데이터에 브랜치 섹션 추가 -- **Priority**: 2 (depends on Todo 1) -- **Dependencies**: Todo 1 -- **Goal**: Task Detail 페이지의 메타데이터 카드에 브랜치 이름 정보를 추가하고 모달 열기 기능 구현 -- **Work**: - - `src/app/[locale]/task/[id]/page.tsx` 파일 수정 - - **서버 컴포넌트 부분** (기존): - - 변경 없음 (task.branchName은 이미 필드로 존재) - - **메타데이터 섹션** (line 126-206): - - `{task.prUrl && ...}` 블록 뒤에, 새로운 섹션 추가: - ```tsx - {task.branchName && ( -
-
{t("branch")}
-
- - {task.branchName} - - -
-
- )} - ``` - - 메타데이터 섹션 상단에 상태 선언 필요: `const [showBranchTasksModal, setShowBranchTasksModal] = useState(false);` - - 메타데이터 카드 하단에 모달 렌더링: `{showBranchTasksModal && }` - - 이를 위해 "use client" 지시문이 필요하므로 클라이언트 래퍼 컴포넌트 고려 -- **Convention Notes**: - - 서버/클라이언트 컴포넌트 분리 고려: 기존 page.tsx는 서버 컴포넌트이므로, TaskDetailSidebar 같은 클라이언트 컴포넌트로 메타데이터 섹션 분리 가능 - - 또는 상태 관리를 위해 기존 page.tsx를 클라이언트 컴포넌트로 변경 (이미 form/state 사용 중이므로 가능) - - 화살표 아이콘: SVG 직접 구현 (기존 TaskCard의 PR 아이콘 스타일 참고) - - 국제화: `t("branch")`, `t("viewOtherTasks")` 등 필요 -- **Verification**: - - Task Detail 페이지 열기 - - branchName이 있는 작업에서 "Branch" 메타데이터 섹션 표시 확인 - - 화살표 버튼 클릭 가능성 확인 - - console 에러 없음 -- **Exit Criteria**: - - 메타데이터 섹션에 브랜치 이름이 tag 형식으로 표시됨 - - 오른쪽 화살표(→) 아이콘이 브랜치명 옆에 표시됨 - - 버튼이 클릭 가능한 상태 (hover 효과 포함) - -### Todo 4: ProjectBranchTasksModal 통합 및 네비게이션 완성 -- **Priority**: 2 (depends on Todo 2, 3) -- **Dependencies**: Todo 2, 3 -- **Goal**: 모달이 정상 작동하며 작업 선택 시 해당 페이지로 이동 -- **Work**: - - Task Detail page에 ProjectBranchTasksModal import: `import ProjectBranchTasksModal from "@/components/ProjectBranchTasksModal";` - - 모달 렌더링 위치: 메타데이터 카드 하단 또는 사이드바 끝 - - Props 전달: - - `projectId={task.projectId || ""}` - - `currentBranchName={task.branchName}` - - `tasks={initialTasks}` (page에서 받은 initialTasks 사용) 또는 API 호출로 다시 조회 - - `onClose={() => setShowBranchTasksModal(false)}` - - `onSelectTask={(taskId) => { router.push(`/task/${taskId}`); }}` - - 라우터 import: `import { useRouter } from "@/i18n/navigation";` - - initialTasks 필터링: 같은 projectId를 가진 작업만 전달 - - 모달 열기: `{showBranchTasksModal && task.projectId && }` -- **Convention Notes**: - - 클라이언트/서버 경계: 필요 시 기존 page.tsx를 클라이언트 컴포넌트로 변경 또는 래퍼 생성 - - 네비게이션: `useRouter().push()` 사용 (next/navigation 대신 @/i18n/navigation) - - 작업 목록: initialTasks에서 필터링하여 사용 (효율성) -- **Verification**: - - 모달 열기 성공 - - 같은 프로젝트의 모든 작업이 모달에 표시됨 - - 작업 클릭 시 해당 Task Detail 페이지로 이동 - - 뒤로가기 버튼으로 이전 페이지 돌아오기 가능 - - 화살표 아이콘 호버 시 커서 변경 -- **Exit Criteria**: - - 모달이 완전히 작동함 - - 작업 선택 후 해당 페이지로 이동 성공 - - UI 일관성 확인 (색상, 텍스트, 레이아웃) - -## Verification Strategy -전체 구현 완료 후 검증: -1. **빌드 성공**: `pnpm build` - TypeScript 컴파일 에러 없음 -2. **색상 변경**: Task Detail 페이지에서 프로젝트 태그 배경색이 #202632(진한 회색)로 표시됨 -3. **브랜치 표시**: branchName이 있는 작업의 메타데이터에 "Branch" 섹션 표시 -4. **모달 열기**: 화살표 아이콘 클릭 시 모달 오픈 -5. **모달 콘텐츠**: 같은 프로젝트의 작업들이 브랜치별로 그룹화되어 표시됨 -6. **네비게이션**: 모달 내 작업 클릭 시 해당 Task Detail 페이지로 이동 -7. **UI 일관성**: 기존 모달/컴포넌트와 스타일 일치 -8. **i18n**: 한국어/영어/중국어 모두 표시 확인 (번역 추가 필요) - -## Progress Tracking -- Total Todos: 4 -- Completed: 4 -- Status: Execution complete - -## Change Log -- 2026-02-17: Plan created -- 2026-02-17: All todos executed successfully - - Todo 1: Design system color token updated (#202632) - - Todo 2: ProjectBranchTasksModal component created - - Todo 3: TaskDetailInfoCard created with branch section - - Todo 4: Modal integration, navigation, and i18n translations added diff --git a/.claude/plan/ui/2602/17-project-group-flow-line.plan.md b/.claude/plan/ui/2602/17-project-group-flow-line.plan.md deleted file mode 100644 index f9bf93a..0000000 --- a/.claude/plan/ui/2602/17-project-group-flow-line.plan.md +++ /dev/null @@ -1,173 +0,0 @@ -# Project Group & Branch Flow Line - -## Business Goal -칸반 보드에서 동일 프로젝트의 태스크를 파스텔톤 테두리로 시각적으로 그룹화하고, 그룹 내에서 baseBranch → childBranch 브랜치 계층 관계를 git-like 트리 라인으로 표시하여 프로젝트별 작업 현황과 브랜치 흐름을 한눈에 파악할 수 있게 한다. - -## Scope -- **In Scope**: - - 컬럼 내 태스크를 프로젝트별로 정렬/그룹화 (같은 프로젝트끼리 인접 배치) - - 프로젝트 그룹에 파스텔톤 테두리 + 프로젝트명 라벨 표시 - - 그룹 내 baseBranch → childBranch 트리 라인/화살표 (같은 컬럼 내에서만) - - 8개 파스텔 색상 자동 할당 (CSS 변수, 프로젝트명 해시 기반) - - worktree 프로젝트는 메인 프로젝트 그룹으로 통합 (기존 projectNameMap 로직 활용) - - 프로젝트 없는 태스크는 그룹 없이 하단에 표시 -- **Out of Scope**: - - 크로스-컬럼 화살표 - - 프로젝트별 색상 수동 설정 (DB 필드) - - 그룹 접기/펼치기 기능 - -## Codebase Analysis Summary -- 칸반 보드 구조: Board → Column → TaskCard (DragDropContext/Droppable/Draggable) -- `KanbanTask` 엔티티에 `projectId`, `baseBranch`, `branchName` 필드 존재 -- `Project` 엔티티에 `defaultBranch`, `isWorktree` 필드 존재 -- `Board.tsx`에서 `projectNameMap` (worktree → 메인 프로젝트명 resolve), `projectFilterSet` 이미 구현 -- Column은 flat list로 TaskCard 렌더링, DnD index는 순서 기반 -- 디자인 토큰: `:root`에 CSS 변수 → `@theme inline`에 Tailwind 등록 패턴 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/app/globals.css` | 디자인 토큰 정의 | Modify - 파스텔 그룹 색상 토큰 추가 | -| `prd/design-system.json` | 디자인 시스템 원본 | Modify - 그룹 색상 토큰 추가 | -| `src/components/Board.tsx` | 메인 보드 컴포넌트 | Modify - projectColorMap 계산, Column에 전달 | -| `src/components/Column.tsx` | 컬럼 컴포넌트 | Modify - 태스크 그룹핑 로직, ProjectTaskGroup 렌더링 | -| `src/components/ProjectTaskGroup.tsx` | 프로젝트 그룹 래퍼 | Create - 파스텔 테두리 + 트리 라인 렌더링 | -| `src/components/TaskCard.tsx` | 태스크 카드 | Reference - 기존 구조 유지 | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| CSS 변수 네이밍 | globals.css | `--color-{category}-{name}` 패턴 | -| Tailwind 등록 | globals.css | `:root` 정의 후 `@theme inline`에 등록 | -| 컴포넌트 | 기존 패턴 | `"use client"` 지시자, default export | -| 주석 | CODE_PRINCIPLES | 한국어 서술형 | -| 네이밍 | CODE_PRINCIPLES | 명확한 전체 단어, 비즈니스 역할 표현 | -| i18n | CLAUDE.md | 새 UI 텍스트는 messages/{locale}.json에 번역 키 추가 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 그룹핑 방식 | 컬럼 내 정렬 후 wrapper div | DnD와 호환, 시각적으로 깔끔 | Overlay, Virtual grouping | -| 색상 할당 | 프로젝트명 해시 → 파스텔 팔레트 인덱스 | 세션 간 일관성, DB 변경 불필요 | 순서 기반, DB 저장 | -| 트리 라인 | CSS border-left + 작은 화살표 SVG | 경량, 추가 라이브러리 불필요 | Full SVG, Canvas | -| 새 컴포넌트 | ProjectTaskGroup.tsx | 그룹 렌더링 책임 분리 (SRP) | Column에 직접 구현 | -| DnD 호환 | Draggable index를 전체 컬럼 기준 유지 | 그룹핑 후에도 DnD 순서 유지 | 그룹 내 local index | - -## Implementation Todos - -### Todo 1: 파스텔 색상 디자인 토큰 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 8개 프로젝트 그룹 파스텔 색상을 CSS 변수로 정의 -- **Work**: - - `src/app/globals.css`의 `:root`에 8개 파스텔 색상 변수 추가: - - `--color-group-0-border`, `--color-group-0-bg` ~ `--color-group-7-border`, `--color-group-7-bg` - - border: 파스텔 중간 톤 (테두리용), bg: 매우 연한 파스텔 (배경용) - - 색상: 핑크, 스카이블루, 민트, 라벤더, 피치, 레몬, 로즈, 세이지 - - `@theme inline` 블록에 동일 변수 Tailwind 등록 - - `--color-group-line`: 트리 라인 색상 (gray-300 수준) - - `prd/design-system.json`에 해당 토큰 추가 -- **Convention Notes**: `--color-{category}-{name}` 패턴, `:root` 정의 → `@theme inline` 등록 -- **Verification**: CSS 파일 문법 오류 없음, `pnpm build` 성공 -- **Exit Criteria**: 8개 그룹 색상 + 트리 라인 색상이 CSS 변수로 정의되고 Tailwind에서 사용 가능 -- **Status**: pending - -### Todo 2: Board에서 projectColorMap 계산 로직 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 프로젝트명 → 색상 인덱스(0-7) 매핑 계산 후 Column에 전달 -- **Work**: - - `src/components/Board.tsx`에 `projectColorMap` useMemo 추가: - - `projectNameMap`에서 unique한 프로젝트명 추출 - - 프로젝트명의 간단한 해시값 → 0-7 인덱스 매핑 (일관성 보장) - - `Record` 타입 (resolvedProjectName → colorIndex) - - `ColumnProps`에 `projectColorMap: Record` 추가 - - Column 렌더링 시 `projectColorMap` prop 전달 -- **Convention Notes**: useMemo로 계산, 기존 `projectNameMap` 의존 -- **Verification**: TypeScript 타입 체크 통과 -- **Exit Criteria**: Board가 Column에 프로젝트별 색상 인덱스를 전달하는 구조 완성 -- **Status**: pending - -### Todo 3: ProjectTaskGroup 컴포넌트 생성 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: 프로젝트 그룹의 파스텔 테두리 + 프로젝트명 라벨 + 브랜치 트리 라인을 렌더링하는 컴포넌트 생성 -- **Work**: - - `src/components/ProjectTaskGroup.tsx` 생성 - - Props: `projectName: string`, `colorIndex: number`, `tasks: KanbanTask[]`, `children: React.ReactNode` - - 렌더링 구조: - - 외부 div: `border-2 rounded-lg p-2 mb-3` + 동적 파스텔 border/bg 색상 - - 상단: 프로젝트명 라벨 (작은 텍스트) - - 내부: children (TaskCard들) - - 브랜치 트리 라인 렌더링: - - `tasks` 배열에서 baseBranch → branchName 관계를 분석하여 트리 구조 계산 - - 루트 노드: baseBranch가 없거나 프로젝트 defaultBranch와 일치하는 태스크 - - 자식 노드: baseBranch가 다른 태스크의 branchName과 일치하는 태스크 - - 각 태스크 카드 왼쪽에 트리 라인 표시 (CSS border-left + pseudo-element) - - 루트: 세로선 시작, 자식: 세로선에서 가로선으로 분기하는 L자 라인 - - 브랜치 관계가 없는 태스크(branchName 없음)는 라인 없이 일반 표시 -- **Convention Notes**: `"use client"`, default export, 한국어 주석 -- **Verification**: 컴포넌트 단독 렌더링 가능, 타입 체크 통과 -- **Exit Criteria**: 파스텔 테두리 + 프로젝트명 + 트리 라인이 포함된 그룹 컴포넌트 완성 -- **Status**: pending - -### Todo 4: Column에서 태스크 그룹핑 및 ProjectTaskGroup 렌더링 -- **Priority**: 3 -- **Dependencies**: Todo 2, Todo 3 -- **Goal**: Column 내 태스크를 프로젝트별로 그룹화하여 ProjectTaskGroup으로 렌더링 -- **Work**: - - `src/components/Column.tsx` 수정 - - `ColumnProps`에 `projectColorMap: Record` 추가 - - 태스크 그룹핑 로직: - - `tasks`를 projectName 기준으로 정렬 (같은 프로젝트끼리 인접) - - 프로젝트 있는 태스크: 그룹별로 `ProjectTaskGroup` 래퍼로 감싸기 - - 프로젝트 없는 태스크: 그룹 없이 하단에 렌더링 - - DnD 호환: - - Draggable `index`는 전체 컬럼 기준 연속 번호 유지 - - 그룹 wrapper div 안에 Draggable TaskCard 배치 (DnD와 호환) - - `Board.tsx`에서 전달하는 `projectColorMap` 활용 -- **Convention Notes**: DnD index 연속성 필수, 그룹 내/외 순서 일관성 -- **Verification**: `pnpm build` 성공, DnD가 그룹핑 후에도 정상 동작 -- **Exit Criteria**: 컬럼 내 태스크가 프로젝트별 파스텔 테두리 그룹으로 표시되고 DnD 동작 유지 -- **Status**: pending - -### Todo 5: i18n 번역 키 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 새로 추가되는 UI 텍스트의 번역 키 등록 -- **Work**: - - `messages/ko.json`에 추가: `"board.noProject": "프로젝트 없음"` (그룹 없는 태스크 영역 라벨, 필요 시) - - `messages/en.json`에 추가: `"board.noProject": "No Project"` - - `messages/zh.json`에 추가: `"board.noProject": "无项目"` - - Assumption: 현재 UI에서 별도 라벨이 필요 없으면 이 Todo는 skip 가능 -- **Convention Notes**: 3개 언어 파일 동시 수정 -- **Verification**: 빌드 시 번역 키 누락 에러 없음 -- **Exit Criteria**: 새 UI에 필요한 번역 키가 3개 언어로 등록됨 -- **Status**: pending - -### Todo 6: 빌드 검증 및 최종 테스트 -- **Priority**: 4 -- **Dependencies**: Todo 4, Todo 5 -- **Goal**: 전체 기능 통합 후 빌드 성공 및 타입 안전성 확인 -- **Work**: - - `pnpm build` 실행하여 전체 빌드 성공 확인 - - TypeScript 타입 에러 없음 확인 - - 기존 테스트 (`pnpm test`) 통과 확인 -- **Convention Notes**: N/A -- **Verification**: `pnpm build && pnpm test` 성공 -- **Exit Criteria**: 빌드 성공, 기존 테스트 통과, 타입 에러 없음 -- **Status**: pending - -## Verification Strategy -- `pnpm build`: 전체 빌드 성공 -- `pnpm test`: 기존 테스트 회귀 없음 -- 시각적 확인: 프로젝트 그룹 파스텔 테두리, 트리 라인 렌더링, DnD 동작 - -## Progress Tracking -- Total Todos: 6 -- Completed: 6 -- Status: Execution complete - -## Change Log -- 2026-02-17: Plan created -- 2026-02-17: All todos executed and verified diff --git a/.claude/plan/ui/2602/17-right-click-create-branch-todo.plan.md b/.claude/plan/ui/2602/17-right-click-create-branch-todo.plan.md deleted file mode 100644 index 0f74e3c..0000000 --- a/.claude/plan/ui/2602/17-right-click-create-branch-todo.plan.md +++ /dev/null @@ -1,108 +0,0 @@ -# Right-click Create Branch TODO - -## Business Goal -기존 태스크 카드를 우클릭하여 해당 태스크의 브랜치를 base로 삼아 새 브랜치 기반 TODO를 생성하는 기능을 추가한다. 이를 통해 기존 작업에서 파생 작업을 빠르게 만들 수 있다. - -## Scope -- **In Scope**: TaskContextMenu에 새 옵션 추가, CreateTaskModal에 defaultBaseBranch prop 추가, Board.tsx 핸들러 연결, 3개 언어 번역 키 추가 -- **Out of Scope**: 컬럼 빈 영역 우클릭, BranchTaskModal 수정, 새 API endpoint 추가 - -## Codebase Analysis Summary -- `TaskContextMenu`: hasBranch prop으로 분기 옵션 표시 제어. 현재 hasBranch일 때는 삭제만 표시됨 -- `CreateTaskModal`: defaultProjectId optional prop 패턴 존재. baseBranch는 프로젝트 선택 시 자동 설정 -- `Board.tsx`: contextMenu state에 task 정보 포함. isBranchModalOpen 패턴으로 모달 제어 -- `createTask` server action: baseBranch를 input으로 받아 처리 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/components/TaskContextMenu.tsx` | 우클릭 컨텍스트 메뉴 | Modify | -| `src/components/CreateTaskModal.tsx` | 작업 생성 모달 | Modify | -| `src/components/Board.tsx` | 메인 보드 컴포넌트 | Modify | -| `messages/ko.json` | 한국어 번역 | Modify | -| `messages/en.json` | 영어 번역 | Modify | -| `messages/zh.json` | 중국어 번역 | Modify | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| i18n 키 사용 | messages/*.json | 하드코딩 텍스트 금지, 3개 언어 동시 추가 | -| CSS 토큰 사용 | globals.css | Tailwind 클래스에서 디자인 토큰 사용 | -| useCallback 패턴 | Board.tsx | 이벤트 핸들러를 useCallback으로 감싸기 | -| Optional prop 패턴 | CreateTaskModal | defaultProjectId처럼 optional prop으로 기본값 전달 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| 모달 | CreateTaskModal 재사용 | 동일한 폼 필드, 코드 중복 방지 | 별도 모달 생성 | -| Base 브랜치 전달 | defaultBaseBranch prop | 기존 defaultProjectId 패턴과 일관 | 별도 state 주입 | -| 메뉴 조건 | hasBranch일 때만 표시 | 브랜치 없는 태스크는 base가 없으므로 의미 없음 | 항상 표시 | - -## Implementation Todos - -### Todo 1: Add i18n translation keys -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 컨텍스트 메뉴의 새 옵션에 대한 번역 키를 3개 언어에 추가 -- **Work**: - - `messages/ko.json`의 `contextMenu` 객체에 `"createBranchTodo": "브랜치 기반 TODO 생성"` 추가 - - `messages/en.json`의 `contextMenu` 객체에 `"createBranchTodo": "Create branch TODO"` 추가 - - `messages/zh.json`의 `contextMenu` 객체에 `"createBranchTodo": "创建分支TODO"` 추가 -- **Convention Notes**: 3개 언어 파일 동시 수정 -- **Verification**: JSON 파싱 오류 없는지 확인 -- **Exit Criteria**: 3개 언어 파일 모두에 동일 키가 존재 -- **Status**: pending - -### Todo 2: Add onCreateBranchTodo callback to TaskContextMenu -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: TaskContextMenu에 새 옵션 UI와 콜백을 추가 -- **Work**: - - `TaskContextMenuProps`에 `onCreateBranchTodo: () => void` prop 추가 - - `hasBranch`일 때 "브랜치 기반 TODO 생성" 버튼 렌더링 (기존 버튼 스타일 따라감) - - `t("createBranchTodo")` 번역 키 사용 -- **Convention Notes**: 기존 버튼 CSS 클래스 패턴(`w-full text-left px-4 py-2 text-sm text-text-primary hover:bg-bg-page transition-colors`) 유지 -- **Verification**: TypeScript 타입 에러 없는지 확인 -- **Exit Criteria**: hasBranch일 때 새 버튼이 표시되고 콜백 호출 -- **Status**: pending - -### Todo 3: Add defaultBaseBranch prop to CreateTaskModal -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: CreateTaskModal이 외부에서 baseBranch 기본값을 받을 수 있도록 확장 -- **Work**: - - `CreateTaskModalProps`에 `defaultBaseBranch?: string` 추가 - - `useEffect`에서 `defaultBaseBranch`가 있으면 `setBaseBranch(defaultBaseBranch)` 호출 - - `defaultBaseBranch`가 있을 때는 baseBranch select에 해당 값이 옵션으로 포함되도록 처리 -- **Convention Notes**: 기존 defaultProjectId 패턴과 동일하게 처리 -- **Verification**: TypeScript 타입 에러 없는지 확인 -- **Exit Criteria**: defaultBaseBranch prop이 전달되면 baseBranch가 미리 설정됨 -- **Status**: pending - -### Todo 4: Wire up Board.tsx handlers -- **Priority**: 2 -- **Dependencies**: Todo 1, 2, 3 -- **Goal**: Board 컴포넌트에서 새 컨텍스트 메뉴 옵션과 CreateTaskModal을 연결 -- **Work**: - - `handleCreateBranchTodo` 콜백 추가 (useCallback): contextMenu를 닫고 CreateTaskModal을 열되 defaultBaseBranch와 defaultProjectId를 설정 - - 새 state 추가: `branchTodoDefaults` (`{ baseBranch: string; projectId: string } | null`) - - `TaskContextMenu`에 `onCreateBranchTodo={handleCreateBranchTodo}` 전달 - - `CreateTaskModal`에 `defaultBaseBranch={branchTodoDefaults?.baseBranch}` 전달 - - 모달 닫힐 때 `branchTodoDefaults` 초기화 -- **Convention Notes**: useCallback 패턴, 기존 handleBranchFromCard 패턴 참조 -- **Verification**: TypeScript 빌드 성공 확인 -- **Exit Criteria**: 우클릭 → "브랜치 기반 TODO 생성" → CreateTaskModal이 baseBranch 미리 채워진 상태로 열림 -- **Status**: pending - -## Verification Strategy -- `pnpm build` 성공 확인 -- 3개 언어 JSON 파일 문법 확인 -- TypeScript 타입 에러 없는지 확인 - -## Progress Tracking -- Total Todos: 4 -- Completed: 0 -- Status: Planning complete - -## Change Log -- 2026-02-17: Plan created diff --git a/.claude/plan/ui/2602/18-project-color-db-depth-opacity.plan.md b/.claude/plan/ui/2602/18-project-color-db-depth-opacity.plan.md deleted file mode 100644 index e616376..0000000 --- a/.claude/plan/ui/2602/18-project-color-db-depth-opacity.plan.md +++ /dev/null @@ -1,176 +0,0 @@ -# Project Color DB 저장 + 자식 브랜치 명암 표현 - -## Business Goal -프로젝트 그룹 색상을 DB에 영속화하여 사용자가 직접 변경할 수 있게 하고, 브랜치 계층에 따른 색상 명암으로 부모-자식 관계를 시각적으로 표현한다. - -## Scope -- **In Scope**: - - Project 엔티티에 `colorIndex` 컬럼 추가 (DB 마이그레이션) - - Board에서 DB colorIndex 우선 사용, null이면 해시 fallback - - TaskDetail 페이지에 프로젝트 색상 인라인 편집기 추가 - - 프로젝트 그룹 내 자식 브랜치 태스크에 depth 기반 opacity 적용 -- **Out of Scope**: - - HEX 커스텀 색상 입력 - - worktree 프로젝트 독립 색상 설정 - -## Codebase Analysis Summary -- Project 엔티티: `src/entities/Project.ts` (id, name, repoPath, defaultBranch, sshHost, isWorktree, createdAt) -- Board의 projectColorMap: useMemo 해시 기반 (projectNameMap → 0-7 인덱스) -- Column: ProjectTaskGroup 래퍼 + BranchConnector + depth 기반 indentation -- TaskDetailInfoCard: PriorityEditor 패턴 참고 가능 -- 마이그레이션: `src/migrations/` 타임스탬프 기반, `database.ts` migrations 배열에 수동 추가 - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/entities/Project.ts` | Project 엔티티 | Modify - colorIndex 추가 | -| `src/migrations/` | DB 마이그레이션 | Create - AddColorIndexToProjects | -| `src/lib/database.ts` | 런타임 DataSource | Modify - migrations 배열에 추가 | -| `src/app/actions/kanban.ts` | Server actions | Modify - updateProjectColor 추가 | -| `src/components/Board.tsx` | 보드 메인 | Modify - projectColorMap DB 값 우선 사용 | -| `src/components/ProjectColorEditor.tsx` | 색상 편집기 | Create - 인라인 색상 선택 UI | -| `src/components/TaskDetailInfoCard.tsx` | Detail 사이드바 | Modify - ProjectColorEditor 통합 | -| `src/components/Column.tsx` | 컬럼 렌더링 | Modify - 자식 브랜치 opacity 적용 | -| `src/components/ProjectTaskGroup.tsx` | 그룹 래퍼 | Modify - depth 기반 스타일 전달 | -| `messages/*.json` | i18n | Modify - 번역 키 추가 | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| 마이그레이션 | CLAUDE.md | 엔티티 수정 → generate → database.ts에 import 추가 | -| Server action | 기존 패턴 | `"use server"`, revalidatePath, serialize | -| 인라인 편집기 | PriorityEditor | 클릭 시 드롭다운, server action 호출 | -| i18n | CLAUDE.md | ko/en/zh 3개 파일 동시 수정 | - -## Architecture Decisions -| Decision | Choice | Rationale | Alternatives | -|----------|--------|-----------|--------------| -| DB 타입 | smallint nullable | null = 해시 fallback, 0-7 범위 | varchar hex | -| 편집 UI | 8색 원형 스와치 드롭다운 | PriorityEditor 패턴 일관, 직관적 | 모달, 슬라이더 | -| 자식 opacity | inline style opacity | CSS 변수 추가 없이 간단 | HSL lightness | -| worktree 색상 | 메인 프로젝트 colorIndex 조회 | 기존 통합 로직 유지 | 개별 설정 | - -## Implementation Todos - -### Todo 1: Project 엔티티 + 마이그레이션 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: Project 테이블에 color_index 컬럼 추가 -- **Work**: - - `src/entities/Project.ts`에 colorIndex 프로퍼티 추가: `@Column({ name: "color_index", type: "smallint", nullable: true, default: null }) colorIndex!: number | null;` - - `pnpm migration:generate -- src/migrations/AddColorIndexToProjects` 실행 - - 생성된 마이그레이션 파일 검토 - - `src/lib/database.ts` migrations 배열에 새 마이그레이션 import 추가 -- **Convention Notes**: 마이그레이션 파일은 생성 후 수정하지 않음 -- **Verification**: `pnpm migration:run` 성공 -- **Exit Criteria**: DB에 color_index 컬럼 존재, 기존 데이터는 null -- **Status**: pending - -### Todo 2: Server action - updateProjectColor -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: 프로젝트 색상 변경 API 추가 -- **Work**: - - `src/app/actions/kanban.ts`에 `updateProjectColor(projectId: string, colorIndex: number)` 추가 - - 동일 repoPath의 worktree 프로젝트도 같은 colorIndex로 업데이트 - - revalidatePath 호출 - - serialize 패턴 따름 -- **Convention Notes**: 기존 server action 패턴, `"use server"`, revalidatePath -- **Verification**: TypeScript 타입 체크 통과 -- **Exit Criteria**: 프로젝트 색상을 DB에 저장하고 관련 worktree도 동기화하는 server action 완성 -- **Status**: pending - -### Todo 3: Board - DB colorIndex 우선 사용 -- **Priority**: 2 -- **Dependencies**: Todo 1 -- **Goal**: projectColorMap에서 DB값 우선, null이면 해시 fallback -- **Work**: - - `src/components/Board.tsx`의 `projectColorMap` useMemo 수정 - - projects 배열에서 메인 프로젝트의 colorIndex를 조회 - - colorIndex가 있으면 사용, null이면 기존 해시 로직 fallback - - worktree 프로젝트는 메인 프로젝트의 colorIndex 상속 -- **Convention Notes**: 기존 projectNameMap, projectColorMap 구조 유지 -- **Verification**: TypeScript 타입 체크 통과 -- **Exit Criteria**: DB에 colorIndex가 저장된 프로젝트는 해당 색상 사용 -- **Status**: pending - -### Todo 4: ProjectColorEditor 컴포넌트 -- **Priority**: 2 -- **Dependencies**: Todo 2 -- **Goal**: 프로젝트 색상을 변경할 수 있는 인라인 편집기 컴포넌트 -- **Work**: - - `src/components/ProjectColorEditor.tsx` 생성 - - Props: `projectId: string`, `currentColorIndex: number | null` - - UI: 현재 색상 원형 표시, 클릭 시 8색 스와치 드롭다운 - - 선택 시 `updateProjectColor` server action 호출 - - PriorityEditor 패턴 참고 (useTransition, 드롭다운) -- **Convention Notes**: `"use client"`, 기존 인라인 편집기 패턴 -- **Verification**: 컴포넌트 렌더링 가능, 타입 체크 통과 -- **Exit Criteria**: 8색 중 선택하여 프로젝트 색상 변경 가능 -- **Status**: pending - -### Todo 5: TaskDetailInfoCard에 색상 편집기 통합 -- **Priority**: 3 -- **Dependencies**: Todo 4 -- **Goal**: Detail 페이지에서 프로젝트 색상 편집 가능 -- **Work**: - - `src/components/TaskDetailInfoCard.tsx` 수정 - - 기존 project 표시 영역에 ProjectColorEditor 추가 - - task.project.colorIndex를 전달 -- **Convention Notes**: 기존 PriorityEditor 배치 패턴과 동일 -- **Verification**: Detail 페이지에서 색상 변경 UI 표시 -- **Exit Criteria**: project 정보 옆에 색상 편집기 표시, 변경 가능 -- **Status**: pending - -### Todo 6: 자식 브랜치 depth 기반 opacity 적용 -- **Priority**: 2 -- **Dependencies**: none -- **Goal**: 프로젝트 그룹 내 자식 태스크의 테두리/배경 색상을 depth에 비례하여 연하게 -- **Work**: - - `src/components/Column.tsx` 수정 - - 그룹 내 각 태스크 렌더링 시 depth에 기반한 opacity 적용 - - depth 0: opacity 100% (원래 색상), depth 1: opacity 65%, depth 2+: opacity 40% - - TaskCard 래퍼 div에 style={{ opacity }} 적용 (그룹 테두리 내부) - - BranchConnector도 동일 opacity 적용 -- **Convention Notes**: inline style 사용 (동적 값) -- **Verification**: 타입 체크 통과 -- **Exit Criteria**: 자식 브랜치 태스크가 시각적으로 연한 색상으로 표시 -- **Status**: pending - -### Todo 7: i18n 번역 키 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: 색상 편집기 관련 번역 키 등록 -- **Work**: - - `messages/ko.json`: `"taskDetail.projectColor": "프로젝트 색상"` - - `messages/en.json`: `"taskDetail.projectColor": "Project Color"` - - `messages/zh.json`: `"taskDetail.projectColor": "项目颜色"` -- **Convention Notes**: 3개 언어 동시 수정 -- **Verification**: 빌드 시 번역 키 누락 에러 없음 -- **Exit Criteria**: 번역 키 등록 완료 -- **Status**: pending - -### Todo 8: 빌드 검증 -- **Priority**: 4 -- **Dependencies**: Todo 3, Todo 5, Todo 6, Todo 7 -- **Goal**: 전체 빌드 + 테스트 통과 -- **Work**: - - `npx tsc --noEmit` TypeScript 검증 - - `pnpm test` 기존 테스트 통과 -- **Verification**: 모든 검증 통과 -- **Exit Criteria**: 타입 에러 0, 테스트 84/84 통과 -- **Status**: pending - -## Verification Strategy -- `npx tsc --noEmit`: TypeScript 타입 안전성 -- `pnpm test`: 기존 테스트 회귀 없음 -- `pnpm migration:run`: 마이그레이션 적용 성공 - -## Progress Tracking -- Total Todos: 8 -- Completed: 8 -- Status: Execution complete - -## Change Log -- 2026-02-18: Plan created -- 2026-02-18: All todos executed and verified diff --git a/.claude/plan/ui/2602/20-edit-description.plan.md b/.claude/plan/ui/2602/20-edit-description.plan.md deleted file mode 100644 index 118d27c..0000000 --- a/.claude/plan/ui/2602/20-edit-description.plan.md +++ /dev/null @@ -1,88 +0,0 @@ -# Edit Description on Task Detail Page - -## Business Goal -게시물 상세 페이지에서 작업 설명(description)을 인라인으로 수정할 수 있도록 하여, 사용자가 별도 폼 없이 빠르게 설명을 추가/변경할 수 있게 한다. - -## Scope -- **In Scope**: description 클릭 시 textarea 전환, 저장/취소 버튼, Ctrl+Enter 저장, Escape 취소, 설명 없을 때 "설명 추가" 플레이스홀더, i18n 3개 언어 번역 키 -- **Out of Scope**: 마크다운 렌더링, 리치 텍스트 에디터, title 수정 - -## Codebase Analysis Summary -- `TaskDetailTitleCard.tsx`: 현재 description을 읽기 전용 `

`로 표시. `"use client"` 이미 적용된 클라이언트 컴포넌트. -- `src/app/actions/kanban.ts`: `updateTask(taskId, { description })` 함수 존재. -- `PriorityEditor.tsx`: `useTransition` + `updateTask` + `router.refresh()` 인라인 수정 패턴. - -### Relevant Files -| File | Role | Action | -|------|------|--------| -| `src/components/TaskDetailTitleCard.tsx` | description 표시 및 수정 UI | Modify | -| `messages/ko.json` | 한국어 번역 | Modify | -| `messages/en.json` | 영어 번역 | Modify | -| `messages/zh.json` | 중국어 번역 | Modify | -| `src/app/actions/kanban.ts` | `updateTask` server action | Reference | - -### Conventions to Follow -| Convention | Source | Rule | -|-----------|--------|------| -| 인라인 수정 패턴 | `PriorityEditor.tsx` | `useTransition` + `updateTask` + `router.refresh()` | -| 디자인 토큰 | `globals.css` | Tailwind CSS 변수 사용 (`bg-bg-surface`, `text-text-secondary` 등) | -| i18n | CLAUDE.md | 3개 언어 동시 추가, `useTranslations` 사용 | - -## Implementation Todos - -### Todo 1: i18n 번역 키 추가 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: description 수정 UI에 필요한 번역 키를 3개 언어 파일에 추가 -- **Work**: - - `messages/ko.json`의 `taskDetail` 네임스페이스에 추가: `editDescription`, `addDescription`, `save`, `cancel` - - `messages/en.json`의 `taskDetail` 네임스페이스에 동일 키 추가 - - `messages/zh.json`의 `taskDetail` 네임스페이스에 동일 키 추가 -- **Convention Notes**: 기존 `taskDetail` 네임스페이스 내 키 위치에 알파벳순 삽입 -- **Verification**: JSON 파싱 에러 없음 확인 -- **Exit Criteria**: 3개 언어 파일에 동일 키가 존재 -- **Status**: pending - -### Todo 2: TaskDetailTitleCard에 description 인라인 수정 기능 구현 -- **Priority**: 1 -- **Dependencies**: none -- **Goal**: description 클릭 시 textarea로 전환되는 인라인 수정 UI 구현 -- **Work**: - - `TaskDetailTitleCard.tsx`에 `isEditing` 상태 추가 - - `updateTask` import, `useRouter`, `useTransition`, `useTranslations` 사용 - - description `

` 클릭 시 `isEditing: true` 전환 - - 편집 모드: `