hjworktree-cli 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +10 -0
- package/.context-snapshots/context-snapshot-20260106-180000.md +108 -0
- package/.context-snapshots/context-snapshot-20260106-235500.md +108 -0
- package/README.md +134 -0
- package/dist/server/routes/api.d.ts.map +1 -1
- package/dist/server/routes/api.js +45 -1
- package/dist/server/routes/api.js.map +1 -1
- package/dist/server/services/worktreeService.d.ts +23 -0
- package/dist/server/services/worktreeService.d.ts.map +1 -1
- package/dist/server/services/worktreeService.js +156 -14
- package/dist/server/services/worktreeService.js.map +1 -1
- package/dist/server/socketHandlers.d.ts.map +1 -1
- package/dist/server/socketHandlers.js +74 -1
- package/dist/server/socketHandlers.js.map +1 -1
- package/dist/shared/types/index.d.ts +36 -0
- package/dist/shared/types/index.d.ts.map +1 -1
- package/dist/web/assets/{index-C61yAbey.css → index-CsixHL-D.css} +1 -1
- package/dist/web/assets/index-D8dr9mJa.js +53 -0
- package/dist/web/assets/index-D8dr9mJa.js.map +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/server/routes/api.ts +51 -2
- package/server/services/worktreeService.ts +181 -13
- package/server/socketHandlers.ts +91 -2
- package/shared/types/index.ts +45 -0
- package/web/src/App.tsx +3 -0
- package/web/src/components/Layout/LeftNavBar.tsx +290 -26
- package/web/src/components/Layout/MainLayout.tsx +2 -6
- package/web/src/components/Modals/AddWorktreeModal.tsx +87 -0
- package/web/src/components/Modals/ConfirmDeleteModal.tsx +114 -0
- package/web/src/components/Modals/ModalContainer.tsx +21 -0
- package/web/src/components/Terminal/TerminalPanel.tsx +32 -37
- package/web/src/stores/useAppStore.ts +200 -3
- package/web/src/styles/global.css +522 -0
- package/dist/web/assets/index-WEdVUKxb.js +0 -53
- package/dist/web/assets/index-WEdVUKxb.js.map +0 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Context Snapshot
|
|
2
|
+
|
|
3
|
+
## Session Metadata
|
|
4
|
+
- **Created**: 2026-01-06T18:00:00+09:00
|
|
5
|
+
- **CLI**: Claude Code
|
|
6
|
+
- **Project**: /root/workspace/work/new/utils/hjWorktree-cli
|
|
7
|
+
|
|
8
|
+
## Conversation Summary
|
|
9
|
+
|
|
10
|
+
### User Requests
|
|
11
|
+
- Delete 버튼 클릭 시 프로젝트의 `.git` 폴더가 삭제되는 치명적 버그 수정
|
|
12
|
+
- Close All 버튼 검수
|
|
13
|
+
- 프로덕션 빌드에서도 동일한 문제가 있는지 확인
|
|
14
|
+
- Plan mode로 분석 후 수정
|
|
15
|
+
|
|
16
|
+
### Completed Tasks
|
|
17
|
+
1. **취약점 분석**: 4개의 치명적 보안 취약점 발견
|
|
18
|
+
- V1: 경로 비교 시 정규화 미적용 (`worktreeService.ts:156`)
|
|
19
|
+
- V2: 심볼릭 링크 미해결 (`path.resolve()` 사용)
|
|
20
|
+
- V3: 위험한 `fs.rm()` 폴백 (git 실패 시 강제 삭제)
|
|
21
|
+
- V4: 메인 워크트리 식별자 부재
|
|
22
|
+
|
|
23
|
+
2. **6겹 방어 시스템 구현**:
|
|
24
|
+
- Layer 1: `realpathSync()` 심볼릭 링크 해결
|
|
25
|
+
- Layer 2: `.git` 파일 vs 디렉토리 검증
|
|
26
|
+
- Layer 3: 경로 정확히 일치 체크
|
|
27
|
+
- Layer 4: 내부 경로 체크
|
|
28
|
+
- Layer 5: 부모 경로 체크 (재앙 방지)
|
|
29
|
+
- Layer 6: `fs.rm()` 폴백 완전 제거
|
|
30
|
+
|
|
31
|
+
3. **`isMainWorktree` 플래그 추가**: 데이터 모델에 메인 워크트리 명시적 식별자 추가
|
|
32
|
+
|
|
33
|
+
4. **API 레벨 403 보호**: 메인 워크트리 삭제 시도 시 즉시 거부
|
|
34
|
+
|
|
35
|
+
5. **빌드 검증**: TypeScript 컴파일 및 Vite 빌드 성공
|
|
36
|
+
|
|
37
|
+
### Modified Files
|
|
38
|
+
- `shared/types/index.ts`
|
|
39
|
+
- `server/services/worktreeService.ts`
|
|
40
|
+
- `server/routes/api.ts`
|
|
41
|
+
|
|
42
|
+
## Technical Context
|
|
43
|
+
|
|
44
|
+
### Key Concepts
|
|
45
|
+
- **Git Worktree**: 메인 워크트리 vs 링크드 워크트리 구분
|
|
46
|
+
- 메인 워크트리: `.git` 디렉토리 보유
|
|
47
|
+
- 링크드 워크트리: `.git` 파일 (메인 .git 경로 포함)
|
|
48
|
+
- **심볼릭 링크 해결**: `path.resolve()` vs `realpathSync()`
|
|
49
|
+
- **Defense in Depth**: 다중 레이어 보안 방어 전략
|
|
50
|
+
|
|
51
|
+
### Important Changes
|
|
52
|
+
|
|
53
|
+
**shared/types/index.ts**
|
|
54
|
+
```typescript
|
|
55
|
+
export interface Worktree {
|
|
56
|
+
path: string;
|
|
57
|
+
branch: string;
|
|
58
|
+
name: string;
|
|
59
|
+
isMainWorktree: boolean; // NEW
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**server/services/worktreeService.ts**
|
|
64
|
+
- `safeRealpath()`: 심볼릭 링크를 안전하게 해결하는 헬퍼 메서드
|
|
65
|
+
- `isLinkedWorktree()`: `.git` 파일 존재 여부로 링크드 워크트리 판별
|
|
66
|
+
- `isMainRepository()`: 메인 저장소 여부 확인
|
|
67
|
+
- `listWorktrees()`: `isMainWorktree` 플래그 설정 추가
|
|
68
|
+
- `removeWorktree()`: 5개 보안 가드 + `fs.rm()` 폴백 제거
|
|
69
|
+
- `removeAllWorktrees()`: 이중 필터링 (flag + realpath)
|
|
70
|
+
- `removeWorktreesByNames()`: 메인 워크트리 삭제 차단
|
|
71
|
+
|
|
72
|
+
**server/routes/api.ts**
|
|
73
|
+
- `DELETE /worktrees/:name`: 메인 워크트리 삭제 시 403 반환
|
|
74
|
+
|
|
75
|
+
### Issues Discovered & Resolved
|
|
76
|
+
|
|
77
|
+
| 취약점 | 심각도 | 해결 방법 |
|
|
78
|
+
|--------|--------|-----------|
|
|
79
|
+
| 경로 정규화 미적용 | CRITICAL | `realpathSync()` 사용 |
|
|
80
|
+
| 심볼릭 링크 미해결 | CRITICAL | `realpathSync()` 사용 |
|
|
81
|
+
| `fs.rm()` 폴백 | CRITICAL | 완전 제거, 에러 throw |
|
|
82
|
+
| 메인 워크트리 미식별 | HIGH | `isMainWorktree` 플래그 추가 |
|
|
83
|
+
|
|
84
|
+
## Next Steps
|
|
85
|
+
|
|
86
|
+
### Completed
|
|
87
|
+
- [x] 보안 수정 구현
|
|
88
|
+
- [x] TypeScript 빌드 검증
|
|
89
|
+
|
|
90
|
+
### Recommendations
|
|
91
|
+
1. **수동 테스트 권장**:
|
|
92
|
+
- 심볼릭 링크로 접근한 프로젝트에서 테스트
|
|
93
|
+
- Close All 버튼 동작 확인
|
|
94
|
+
- 단일 세션 삭제 동작 확인
|
|
95
|
+
|
|
96
|
+
2. **버전 업데이트**: 보안 수정이므로 `v2.3.0` 릴리스 권장
|
|
97
|
+
|
|
98
|
+
3. **테스트 케이스 추가 고려**:
|
|
99
|
+
- 메인 워크트리 삭제 시도 시 403 반환 확인
|
|
100
|
+
- 심볼릭 링크 프로젝트에서 경로 해결 확인
|
|
101
|
+
- `.git` 디렉토리 vs 파일 구분 확인
|
|
102
|
+
|
|
103
|
+
### Caution
|
|
104
|
+
- `fs.rm()` 폴백이 제거되었으므로, `git worktree remove` 실패 시 에러가 throw됨
|
|
105
|
+
- 이전에는 silent하게 무시되던 에러가 이제 명시적으로 보고됨
|
|
106
|
+
|
|
107
|
+
## Plan File Reference
|
|
108
|
+
- `/root/.claude/plans/encapsulated-swimming-pine.md`
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Context Snapshot
|
|
2
|
+
|
|
3
|
+
## Session Metadata
|
|
4
|
+
- **Created**: 2026-01-06T23:55:00+09:00
|
|
5
|
+
- **CLI**: Claude Code
|
|
6
|
+
- **Project**: /root/workspace/work/new/utils/hjWorktree-cli
|
|
7
|
+
|
|
8
|
+
## Conversation Summary
|
|
9
|
+
|
|
10
|
+
### User Requests
|
|
11
|
+
1. npm에 hjWorktree-cli 프로젝트 배포하기 위한 안내
|
|
12
|
+
2. package.json에 author 필드 추가
|
|
13
|
+
3. 포트 충돌 문제 해결 (EADDRINUSE)
|
|
14
|
+
4. LNB(Left Navigation Bar) UX 대폭 개선
|
|
15
|
+
- LNB가 running 단계에서도 항상 표시
|
|
16
|
+
- 세션 네비게이션을 LNB를 통해 클릭으로 이동
|
|
17
|
+
- 브랜치별 세션 그룹핑
|
|
18
|
+
- 개별/대량 세션 삭제 기능
|
|
19
|
+
- 실행 중 새 워크트리 추가 기능
|
|
20
|
+
5. npm publish를 위한 version 업그레이드 및 README.md 작성
|
|
21
|
+
6. 변경사항 커밋
|
|
22
|
+
|
|
23
|
+
### Completed Tasks
|
|
24
|
+
1. npm publish 프로세스 안내 (2FA, OTP, Access Token 설명)
|
|
25
|
+
2. author 필드 추가: "hyungju-lee <beegizee1220@gmail.com>"
|
|
26
|
+
3. 포트 3847 사용 중인 프로세스 종료 (PID 4442)
|
|
27
|
+
4. LNB 개선 구현 (총 9개 태스크)
|
|
28
|
+
- 타입 정의 추가 (WorktreeSession, SessionGroup, ModalType 등)
|
|
29
|
+
- REST API 엔드포인트 추가 (POST /worktrees/single, DELETE /worktrees/batch)
|
|
30
|
+
- WorktreeService 메서드 추가 (createSingleWorktree, removeWorktreesByNames)
|
|
31
|
+
- Zustand 스토어 확장 (세션 관리, LNB 상태, 모달 상태)
|
|
32
|
+
- LeftNavBar 완전 재작성 (SetupSection, SessionsSection, BulkActionsBar)
|
|
33
|
+
- MainLayout 수정 (LNB 항상 표시)
|
|
34
|
+
- TerminalPanel 수정 ("Back to Setup" 제거, sessionId 기반 전환)
|
|
35
|
+
- 모달 컴포넌트 생성 (AddWorktreeModal, ConfirmDeleteModal, ModalContainer)
|
|
36
|
+
- CSS 스타일 추가 (~500줄)
|
|
37
|
+
5. package.json version 2.0.0 → 2.1.0 업그레이드
|
|
38
|
+
6. README.md 작성
|
|
39
|
+
7. Git 커밋 완료 (1d4476fc)
|
|
40
|
+
|
|
41
|
+
### Modified Files
|
|
42
|
+
- `shared/types/index.ts` - 세션 관리용 타입 정의 추가
|
|
43
|
+
- `server/routes/api.ts` - 2개의 새 API 엔드포인트 추가
|
|
44
|
+
- `server/services/worktreeService.ts` - 2개의 새 메서드 추가
|
|
45
|
+
- `web/src/stores/useAppStore.ts` - 상태 및 액션 대폭 확장
|
|
46
|
+
- `web/src/components/Layout/LeftNavBar.tsx` - 완전 재작성
|
|
47
|
+
- `web/src/components/Layout/MainLayout.tsx` - LNB 항상 표시 로직
|
|
48
|
+
- `web/src/components/Terminal/TerminalPanel.tsx` - sessionId 기반 전환
|
|
49
|
+
- `web/src/components/Modals/AddWorktreeModal.tsx` - 신규 생성
|
|
50
|
+
- `web/src/components/Modals/ConfirmDeleteModal.tsx` - 신규 생성
|
|
51
|
+
- `web/src/components/Modals/ModalContainer.tsx` - 신규 생성
|
|
52
|
+
- `web/src/App.tsx` - ModalContainer 추가
|
|
53
|
+
- `web/src/styles/global.css` - LNB, 세션, 모달 스타일 추가
|
|
54
|
+
- `package.json` - version 2.1.0, author 추가
|
|
55
|
+
- `README.md` - 신규 생성
|
|
56
|
+
|
|
57
|
+
## Technical Context
|
|
58
|
+
|
|
59
|
+
### Key Concepts
|
|
60
|
+
- **Git Worktree**: 단일 저장소에서 여러 브랜치를 동시에 체크아웃
|
|
61
|
+
- **Socket.IO + node-pty**: 실시간 터미널 에뮬레이션
|
|
62
|
+
- **Zustand**: React 상태 관리
|
|
63
|
+
- **xterm.js**: 웹 기반 터미널 렌더링
|
|
64
|
+
- **REST API 설계**: 단일/배치 CRUD 엔드포인트
|
|
65
|
+
|
|
66
|
+
### Important Changes
|
|
67
|
+
|
|
68
|
+
**shared/types/index.ts**
|
|
69
|
+
```typescript
|
|
70
|
+
// 새 타입 정의
|
|
71
|
+
export interface CreateSingleWorktreeRequest { branch: string; agentType: AgentId; }
|
|
72
|
+
export interface BatchDeleteRequest { names: string[]; }
|
|
73
|
+
export interface WorktreeSession extends TerminalInfo { id: string; createdAt: number; }
|
|
74
|
+
export interface SessionGroup { branchName: string; sessions: WorktreeSession[]; isCollapsed: boolean; }
|
|
75
|
+
export type ModalType = 'addWorktree' | 'confirmDelete' | null;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**server/routes/api.ts**
|
|
79
|
+
```typescript
|
|
80
|
+
// POST /worktrees/single - 단일 워크트리 생성
|
|
81
|
+
// DELETE /worktrees/batch - 배치 삭제
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**web/src/stores/useAppStore.ts**
|
|
85
|
+
- 새 상태: activeSessionId, selectedSessionIds, isSetupCollapsed, collapsedBranches, modalType, modalData
|
|
86
|
+
- 새 액션: setActiveSession, toggleSessionSelection, selectAllSessions, clearSessionSelection, removeSelectedSessions, getSessionGroups, toggleSetupCollapse, toggleBranchCollapse, openModal, closeModal, createSingleWorktree
|
|
87
|
+
|
|
88
|
+
**web/src/components/Layout/LeftNavBar.tsx**
|
|
89
|
+
- SetupSection: 접을 수 있는 설정 섹션
|
|
90
|
+
- SessionsSection: 브랜치별 그룹핑된 세션 목록
|
|
91
|
+
- SessionItem: 체크박스, 상태 표시, 삭제 버튼
|
|
92
|
+
- BulkActionsBar: 대량 선택/삭제 기능
|
|
93
|
+
|
|
94
|
+
### Issues Resolved
|
|
95
|
+
1. **npm publish E403 (2FA required)**: 2FA 또는 Automation Token 필요 설명
|
|
96
|
+
2. **OTP vs Access Token 혼동**: 차이점 설명 (6자리 숫자 vs 토큰)
|
|
97
|
+
3. **EADDRINUSE :3847**: `lsof -i :3847` → `kill 4442`로 해결
|
|
98
|
+
4. **LNB가 running 상태에서 숨겨짐**: MainLayout에서 조건부 렌더링 제거
|
|
99
|
+
5. **Back to Setup이 모든 워크트리 삭제**: 버튼 제거, 자유 네비게이션 구현
|
|
100
|
+
|
|
101
|
+
## Next Steps
|
|
102
|
+
- `git push`로 원격 저장소에 푸시
|
|
103
|
+
- `npm publish --access public`로 npm 배포
|
|
104
|
+
- 배포 후 `npm i hjworktree-cli -g`로 설치 테스트
|
|
105
|
+
|
|
106
|
+
## Notes
|
|
107
|
+
- npm 토큰이 채팅에 노출되었으므로 revoke 후 재생성 필요 (이미 처리됨)
|
|
108
|
+
- 현재 브랜치는 `origin/main`보다 1 commit 앞서 있음
|
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# hjWorktree CLI
|
|
2
|
+
|
|
3
|
+
Web-based git worktree manager for running multiple AI coding agents in parallel.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multiple AI Agents Support**: Run Codex CLI, Claude Code, and Gemini CLI simultaneously
|
|
8
|
+
- **Git Worktree Management**: Create, manage, and delete worktrees with automatic cleanup
|
|
9
|
+
- **Real-time Terminal**: xterm.js terminals with full PTY support via Socket.IO
|
|
10
|
+
- **Session Management**:
|
|
11
|
+
- Always-visible LNB (Left Navigation Bar) for session navigation
|
|
12
|
+
- Branch-grouped session display
|
|
13
|
+
- Individual and bulk session deletion
|
|
14
|
+
- Add new worktrees while sessions are running
|
|
15
|
+
- **Modern Web UI**: React + Zustand state management
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g hjworktree-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Requirements**: Node.js >= 20.0.0
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
Navigate to your git repository and run:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
cd /path/to/your/project
|
|
31
|
+
hjWorktree
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This opens a web UI in your browser where you can:
|
|
35
|
+
|
|
36
|
+
1. **Select Branch**: Choose the base branch for worktrees
|
|
37
|
+
2. **Select Agent**: Pick an AI coding agent (Codex, Claude, Gemini)
|
|
38
|
+
3. **Set Worktree Count**: Specify how many parallel worktrees to create
|
|
39
|
+
4. **Run**: Launch terminals with AI agents in each worktree
|
|
40
|
+
|
|
41
|
+
## UI Overview
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
+---------------------------+-----------------------------------+
|
|
45
|
+
| SETUP [−] | |
|
|
46
|
+
| ① Branch: main | |
|
|
47
|
+
| ② Agent: Claude Code | Terminal View |
|
|
48
|
+
| ③ Count: 3 | |
|
|
49
|
+
+---------------------------+ |
|
|
50
|
+
| SESSIONS | |
|
|
51
|
+
| ▼ main (3) | |
|
|
52
|
+
| ● main-project-1 | |
|
|
53
|
+
| ○ main-project-2 | |
|
|
54
|
+
| ○ main-project-3 | |
|
|
55
|
+
| | |
|
|
56
|
+
| [+ Add Session] | |
|
|
57
|
+
+---------------------------+-----------------------------------+
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Supported AI Agents
|
|
61
|
+
|
|
62
|
+
| Agent | Command | Description |
|
|
63
|
+
|-------|---------|-------------|
|
|
64
|
+
| Codex CLI | `codex` | OpenAI Codex CLI agent |
|
|
65
|
+
| Claude Code | `claude` | Anthropic Claude Code agent |
|
|
66
|
+
| Gemini CLI | `gemini` | Google Gemini CLI agent |
|
|
67
|
+
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Clone and install
|
|
72
|
+
git clone https://github.com/user/hjworktree-cli.git
|
|
73
|
+
cd hjworktree-cli
|
|
74
|
+
npm install
|
|
75
|
+
|
|
76
|
+
# Development mode (server + web)
|
|
77
|
+
npm run dev
|
|
78
|
+
|
|
79
|
+
# Build for production
|
|
80
|
+
npm run build
|
|
81
|
+
|
|
82
|
+
# Run production server
|
|
83
|
+
npm run start
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Architecture
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
hjWorktree-cli/
|
|
90
|
+
├── bin/cli.js # CLI entry point
|
|
91
|
+
├── server/ # Express + Socket.IO backend
|
|
92
|
+
│ ├── index.ts # Server entry
|
|
93
|
+
│ ├── socketHandlers.ts # Terminal PTY management
|
|
94
|
+
│ ├── routes/api.ts # REST API
|
|
95
|
+
│ └── services/ # Git/Worktree services
|
|
96
|
+
├── web/ # React frontend
|
|
97
|
+
│ ├── src/components/ # UI components
|
|
98
|
+
│ ├── src/hooks/ # Custom hooks
|
|
99
|
+
│ └── src/stores/ # Zustand stores
|
|
100
|
+
└── shared/ # Shared types/constants
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## API Endpoints
|
|
104
|
+
|
|
105
|
+
| Method | Endpoint | Description |
|
|
106
|
+
|--------|----------|-------------|
|
|
107
|
+
| GET | /api/info | Project info |
|
|
108
|
+
| GET | /api/branches | List branches |
|
|
109
|
+
| POST | /api/worktrees | Create worktrees |
|
|
110
|
+
| POST | /api/worktrees/single | Create single worktree |
|
|
111
|
+
| DELETE | /api/worktrees/:name | Delete worktree |
|
|
112
|
+
| DELETE | /api/worktrees/batch | Batch delete worktrees |
|
|
113
|
+
| DELETE | /api/worktrees | Delete all worktrees |
|
|
114
|
+
|
|
115
|
+
## Changelog
|
|
116
|
+
|
|
117
|
+
### v2.2.0
|
|
118
|
+
- **Security Fix**: Added critical protection guards to prevent accidental deletion of main repository's `.git` folder during worktree cleanup
|
|
119
|
+
- **Session Reliability**: Added path and shell validation before PTY spawn
|
|
120
|
+
- **PTY Stability**: Added retry logic with exponential backoff for PTY spawn failures
|
|
121
|
+
- **Resource Protection**: Added spawn interval control (150ms minimum) to prevent resource contention when creating multiple terminals
|
|
122
|
+
|
|
123
|
+
### v2.1.0
|
|
124
|
+
- Improved LNB with session management and always-visible navigation
|
|
125
|
+
- Implemented 4-step wizard navigation with LNB
|
|
126
|
+
- Rebuilt as web-based application
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT
|
|
131
|
+
|
|
132
|
+
## Author
|
|
133
|
+
|
|
134
|
+
hyungju-lee <beegizee1220@gmail.com>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../server/routes/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAKpD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../server/routes/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAKpD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAuJ7C"}
|
|
@@ -48,7 +48,7 @@ export function apiRouter(cwd) {
|
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
|
-
// Create worktrees
|
|
51
|
+
// Create worktrees (multiple)
|
|
52
52
|
router.post('/worktrees', async (req, res) => {
|
|
53
53
|
try {
|
|
54
54
|
const { branch, count } = req.body;
|
|
@@ -65,6 +65,44 @@ export function apiRouter(cwd) {
|
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
});
|
|
68
|
+
// Create single worktree
|
|
69
|
+
router.post('/worktrees/single', async (req, res) => {
|
|
70
|
+
try {
|
|
71
|
+
const { branch } = req.body;
|
|
72
|
+
if (!branch) {
|
|
73
|
+
res.status(400).json({ error: 'Invalid request: branch is required' });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const worktree = await worktreeService.createSingleWorktree(branch);
|
|
77
|
+
res.json({ worktree });
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
res.status(500).json({
|
|
81
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
// Batch delete worktrees
|
|
86
|
+
router.delete('/worktrees/batch', async (req, res) => {
|
|
87
|
+
try {
|
|
88
|
+
const { names } = req.body;
|
|
89
|
+
if (!names || !Array.isArray(names) || names.length === 0) {
|
|
90
|
+
res.status(400).json({ error: 'Invalid request: names array is required' });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const result = await worktreeService.removeWorktreesByNames(names);
|
|
94
|
+
res.json({
|
|
95
|
+
success: result.failed.length === 0,
|
|
96
|
+
deleted: result.deleted,
|
|
97
|
+
failed: result.failed,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
res.status(500).json({
|
|
102
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
});
|
|
68
106
|
// Delete a worktree
|
|
69
107
|
router.delete('/worktrees/:name', async (req, res) => {
|
|
70
108
|
try {
|
|
@@ -75,6 +113,12 @@ export function apiRouter(cwd) {
|
|
|
75
113
|
res.status(404).json({ error: 'Worktree not found' });
|
|
76
114
|
return;
|
|
77
115
|
}
|
|
116
|
+
// SECURITY: Never delete main worktree
|
|
117
|
+
if (worktree.isMainWorktree) {
|
|
118
|
+
console.warn(`[SECURITY] API blocked attempt to delete main worktree: ${name}`);
|
|
119
|
+
res.status(403).json({ error: 'Cannot delete main worktree' });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
78
122
|
await worktreeService.removeWorktree(worktree.path);
|
|
79
123
|
res.json({ success: true });
|
|
80
124
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../server/routes/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAGjE,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;IACxB,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,eAAe,GAAG,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;IAEjD,mBAAmB;IACnB,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QACxD,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,eAAe,EAAE,CAAC;YACrD,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,UAAU,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAE7E,GAAG,CAAC,IAAI,CAAC;gBACP,GAAG;gBACH,eAAe,EAAE,SAAS;gBAC1B,aAAa;aACd,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;aAChE,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,eAAe;IACf,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAC5D,IAAI,CAAC;YACH,iCAAiC;YACjC,MAAM,UAAU,CAAC,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,WAAW,EAAE,CAAC;YAChD,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;aAChE,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,gBAAgB;IAChB,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,EAAE;QAC7D,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,aAAa,EAAE,CAAC;YACxD,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;aAChE,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../server/routes/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,11 +4,34 @@ export declare class WorktreeService {
|
|
|
4
4
|
private rootDir;
|
|
5
5
|
private createdWorktrees;
|
|
6
6
|
constructor(cwd?: string);
|
|
7
|
+
/**
|
|
8
|
+
* Safely resolves a path to its real path, handling symlinks.
|
|
9
|
+
* Returns null if path cannot be resolved.
|
|
10
|
+
*/
|
|
11
|
+
private safeRealpath;
|
|
12
|
+
/**
|
|
13
|
+
* Checks if a path is a linked worktree (has .git file) vs main repo (has .git directory).
|
|
14
|
+
* Linked worktrees have a .git FILE that points to the main .git directory.
|
|
15
|
+
* Main repo has a .git DIRECTORY.
|
|
16
|
+
*/
|
|
17
|
+
private isLinkedWorktree;
|
|
18
|
+
/**
|
|
19
|
+
* Checks if the given path is the main repository.
|
|
20
|
+
*/
|
|
21
|
+
private isMainRepository;
|
|
7
22
|
listWorktrees(): Promise<Worktree[]>;
|
|
8
23
|
createWorktree(baseBranch: string, index: number): Promise<Worktree>;
|
|
9
24
|
createMultipleWorktrees(baseBranch: string, count: number): Promise<Worktree[]>;
|
|
10
25
|
removeWorktree(worktreePath: string): Promise<void>;
|
|
11
26
|
removeAllWorktrees(): Promise<void>;
|
|
27
|
+
createSingleWorktree(baseBranch: string): Promise<Worktree>;
|
|
28
|
+
removeWorktreesByNames(names: string[]): Promise<{
|
|
29
|
+
deleted: string[];
|
|
30
|
+
failed: {
|
|
31
|
+
name: string;
|
|
32
|
+
error: string;
|
|
33
|
+
}[];
|
|
34
|
+
}>;
|
|
12
35
|
cleanup(): Promise<void>;
|
|
13
36
|
getCreatedWorktrees(): string[];
|
|
14
37
|
getRootDir(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktreeService.d.ts","sourceRoot":"","sources":["../../../server/services/worktreeService.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"worktreeService.d.ts","sourceRoot":"","sources":["../../../server/services/worktreeService.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAA0B;gBAEtC,GAAG,GAAE,MAAsB;IAKvC;;;OAGG;IACH,OAAO,CAAC,YAAY;IAQpB;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAYxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAYlB,aAAa,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAgCpC,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAuCpE,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IA2B/E,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsEnD,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IAgCnC,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAqB3D,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QACrD,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KAC3C,CAAC;IAmCI,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAY9B,mBAAmB,IAAI,MAAM,EAAE;IAI/B,UAAU,IAAI,MAAM;CAGrB;AAKD,wBAAgB,kBAAkB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,eAAe,CAKhE"}
|