hjworktree-cli 2.1.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 +2 -6
- package/.context-snapshots/context-snapshot-20260106-180000.md +108 -0
- package/.context-snapshots/context-snapshot-20260106-235500.md +108 -0
- package/README.md +13 -0
- package/dist/server/routes/api.d.ts.map +1 -1
- package/dist/server/routes/api.js +6 -0
- package/dist/server/routes/api.js.map +1 -1
- package/dist/server/services/worktreeService.d.ts +15 -0
- package/dist/server/services/worktreeService.d.ts.map +1 -1
- package/dist/server/services/worktreeService.js +114 -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 +1 -0
- package/dist/shared/types/index.d.ts.map +1 -1
- package/dist/web/assets/index-D8dr9mJa.js.map +1 -1
- package/package.json +1 -1
- package/server/routes/api.ts +7 -0
- package/server/services/worktreeService.ts +129 -13
- package/server/socketHandlers.ts +91 -2
- package/shared/types/index.ts +1 -0
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"Bash(
|
|
5
|
-
"Bash(netstat:*)",
|
|
6
|
-
"Bash(ss:*)",
|
|
7
|
-
"Bash(kill:*)",
|
|
4
|
+
"Bash(git worktree list:*)",
|
|
8
5
|
"mcp__sequential-thinking__sequentialthinking",
|
|
9
6
|
"Bash(npm run build:*)",
|
|
10
|
-
"Bash(
|
|
11
|
-
"Bash(git commit -m \"$\\(cat <<''EOF''\nfeat: improve LNB with session management and always-visible navigation\n\n- Add always-visible LNB that persists during running state\n- Implement session grouping by branch with collapsible sections\n- Add individual and bulk session selection/deletion\n- Create AddWorktreeModal for adding sessions while running\n- Create ConfirmDeleteModal for safe deletion confirmation\n- Add REST API endpoints: POST /worktrees/single, DELETE /worktrees/batch\n- Remove \"Back to Setup\" button, enable free navigation\n- Add README.md for npm package documentation\n- Bump version to 2.1.0\n\n๐ค Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")"
|
|
7
|
+
"Bash(npm install:*)"
|
|
12
8
|
]
|
|
13
9
|
}
|
|
14
10
|
}
|
|
@@ -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
CHANGED
|
@@ -112,6 +112,19 @@ hjWorktree-cli/
|
|
|
112
112
|
| DELETE | /api/worktrees/batch | Batch delete worktrees |
|
|
113
113
|
| DELETE | /api/worktrees | Delete all worktrees |
|
|
114
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
|
+
|
|
115
128
|
## License
|
|
116
129
|
|
|
117
130
|
MIT
|
|
@@ -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"}
|
|
@@ -113,6 +113,12 @@ export function apiRouter(cwd) {
|
|
|
113
113
|
res.status(404).json({ error: 'Worktree not found' });
|
|
114
114
|
return;
|
|
115
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
|
+
}
|
|
116
122
|
await worktreeService.removeWorktree(worktree.path);
|
|
117
123
|
res.json({ success: true });
|
|
118
124
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../server/routes/api.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../server/routes/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,21 @@ 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[]>;
|
|
@@ -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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { simpleGit } from 'simple-git';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
|
+
import { realpathSync, statSync } from 'fs';
|
|
4
5
|
export class WorktreeService {
|
|
5
6
|
git;
|
|
6
7
|
rootDir;
|
|
@@ -9,20 +10,68 @@ export class WorktreeService {
|
|
|
9
10
|
this.rootDir = cwd;
|
|
10
11
|
this.git = simpleGit(cwd);
|
|
11
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Safely resolves a path to its real path, handling symlinks.
|
|
15
|
+
* Returns null if path cannot be resolved.
|
|
16
|
+
*/
|
|
17
|
+
safeRealpath(targetPath) {
|
|
18
|
+
try {
|
|
19
|
+
return realpathSync(targetPath);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Checks if a path is a linked worktree (has .git file) vs main repo (has .git directory).
|
|
27
|
+
* Linked worktrees have a .git FILE that points to the main .git directory.
|
|
28
|
+
* Main repo has a .git DIRECTORY.
|
|
29
|
+
*/
|
|
30
|
+
isLinkedWorktree(worktreePath) {
|
|
31
|
+
const gitPath = path.join(worktreePath, '.git');
|
|
32
|
+
try {
|
|
33
|
+
const stat = statSync(gitPath);
|
|
34
|
+
// .git is a directory = main repository
|
|
35
|
+
// .git is a file = linked worktree
|
|
36
|
+
return !stat.isDirectory();
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Checks if the given path is the main repository.
|
|
44
|
+
*/
|
|
45
|
+
isMainRepository(targetPath) {
|
|
46
|
+
const resolvedTarget = this.safeRealpath(targetPath);
|
|
47
|
+
const resolvedRoot = this.safeRealpath(this.rootDir);
|
|
48
|
+
if (!resolvedTarget || !resolvedRoot) {
|
|
49
|
+
// If we can't resolve paths, assume it might be main repo (fail safe)
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return resolvedTarget === resolvedRoot;
|
|
53
|
+
}
|
|
12
54
|
async listWorktrees() {
|
|
13
55
|
const result = await this.git.raw(['worktree', 'list', '--porcelain']);
|
|
14
56
|
const worktrees = [];
|
|
15
57
|
const entries = result.trim().split('\n\n').filter(Boolean);
|
|
58
|
+
const resolvedRootDir = this.safeRealpath(this.rootDir);
|
|
16
59
|
for (const entry of entries) {
|
|
17
60
|
const lines = entry.split('\n');
|
|
18
61
|
const worktreePath = lines.find((l) => l.startsWith('worktree '))?.replace('worktree ', '');
|
|
19
62
|
const branchLine = lines.find((l) => l.startsWith('branch '));
|
|
20
63
|
const branch = branchLine?.replace('branch refs/heads/', '');
|
|
21
64
|
if (worktreePath) {
|
|
65
|
+
// Determine if this is the main worktree using realpath comparison
|
|
66
|
+
const resolvedWorktreePath = this.safeRealpath(worktreePath);
|
|
67
|
+
const isMainWorktree = resolvedWorktreePath !== null &&
|
|
68
|
+
resolvedRootDir !== null &&
|
|
69
|
+
resolvedWorktreePath === resolvedRootDir;
|
|
22
70
|
worktrees.push({
|
|
23
71
|
path: worktreePath,
|
|
24
72
|
branch: branch || 'detached',
|
|
25
73
|
name: path.basename(worktreePath),
|
|
74
|
+
isMainWorktree,
|
|
26
75
|
});
|
|
27
76
|
}
|
|
28
77
|
}
|
|
@@ -60,6 +109,7 @@ export class WorktreeService {
|
|
|
60
109
|
path: worktreePath,
|
|
61
110
|
branch: newBranchName,
|
|
62
111
|
name: worktreeName,
|
|
112
|
+
isMainWorktree: false, // Created worktrees are always linked, not main
|
|
63
113
|
};
|
|
64
114
|
}
|
|
65
115
|
async createMultipleWorktrees(baseBranch, count) {
|
|
@@ -88,28 +138,58 @@ export class WorktreeService {
|
|
|
88
138
|
}
|
|
89
139
|
}
|
|
90
140
|
async removeWorktree(worktreePath) {
|
|
141
|
+
// SECURITY GUARD 1: Use realpathSync for symlink resolution
|
|
142
|
+
const resolvedWorktreePath = this.safeRealpath(worktreePath);
|
|
143
|
+
const resolvedRootDir = this.safeRealpath(this.rootDir);
|
|
144
|
+
// If we cannot resolve paths, abort for safety
|
|
145
|
+
if (!resolvedWorktreePath) {
|
|
146
|
+
console.warn(`[SECURITY] Cannot resolve worktree path: ${worktreePath}. Aborting deletion.`);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (!resolvedRootDir) {
|
|
150
|
+
console.warn(`[SECURITY] Cannot resolve root directory path. Aborting deletion.`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// SECURITY GUARD 2: Check exact match with main repo
|
|
154
|
+
if (resolvedWorktreePath === resolvedRootDir) {
|
|
155
|
+
console.warn(`[SECURITY] Attempted to remove main repository: ${worktreePath}. Ignoring.`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// SECURITY GUARD 3: Check if path is inside main repo
|
|
159
|
+
if (resolvedWorktreePath.startsWith(resolvedRootDir + path.sep)) {
|
|
160
|
+
console.warn(`[SECURITY] Attempted to remove path inside main repository: ${worktreePath}. Ignoring.`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// SECURITY GUARD 4: Check if main repo is inside target path (catastrophic prevention)
|
|
164
|
+
if (resolvedRootDir.startsWith(resolvedWorktreePath + path.sep)) {
|
|
165
|
+
console.warn(`[SECURITY] Attempted to remove parent of main repository: ${worktreePath}. Ignoring.`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
// SECURITY GUARD 5: Verify it's actually a linked worktree (has .git FILE, not directory)
|
|
169
|
+
const isLinked = this.isLinkedWorktree(worktreePath);
|
|
170
|
+
if (!isLinked) {
|
|
171
|
+
console.warn(`[SECURITY] Path is not a linked worktree (missing .git file or has .git directory): ${worktreePath}. Ignoring.`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
91
174
|
const branchName = path.basename(worktreePath);
|
|
175
|
+
// CRITICAL: Only use git worktree remove - NEVER use fs.rm() as fallback
|
|
92
176
|
try {
|
|
93
|
-
// 1. Force remove worktree
|
|
94
177
|
await this.git.raw(['worktree', 'remove', worktreePath, '--force']);
|
|
95
178
|
}
|
|
96
|
-
catch {
|
|
97
|
-
//
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
// Ignore errors
|
|
103
|
-
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
// Log the error but DO NOT fall back to fs.rm
|
|
181
|
+
console.error(`[SECURITY] Failed to remove worktree via git: ${worktreePath}`, error);
|
|
182
|
+
// Propagate error instead of dangerous fallback
|
|
183
|
+
throw new Error(`Failed to remove worktree: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
104
184
|
}
|
|
105
|
-
//
|
|
185
|
+
// Prune orphan worktree references
|
|
106
186
|
try {
|
|
107
187
|
await this.git.raw(['worktree', 'prune']);
|
|
108
188
|
}
|
|
109
189
|
catch {
|
|
110
|
-
// Ignore prune errors
|
|
190
|
+
// Ignore prune errors - non-critical
|
|
111
191
|
}
|
|
112
|
-
//
|
|
192
|
+
// Delete the branch
|
|
113
193
|
try {
|
|
114
194
|
await this.git.branch(['-D', branchName]);
|
|
115
195
|
}
|
|
@@ -120,8 +200,22 @@ export class WorktreeService {
|
|
|
120
200
|
}
|
|
121
201
|
async removeAllWorktrees() {
|
|
122
202
|
const worktrees = await this.listWorktrees();
|
|
123
|
-
// Filter out the main worktree
|
|
124
|
-
const additionalWorktrees = worktrees.filter(wt =>
|
|
203
|
+
// Filter out the main worktree using the isMainWorktree flag and path comparison
|
|
204
|
+
const additionalWorktrees = worktrees.filter(wt => {
|
|
205
|
+
// Primary check: use isMainWorktree flag
|
|
206
|
+
if (wt.isMainWorktree) {
|
|
207
|
+
console.log(`[SECURITY] Skipping main worktree in removeAll: ${wt.path}`);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
// Secondary check: realpath comparison as defense in depth
|
|
211
|
+
const resolvedPath = this.safeRealpath(wt.path);
|
|
212
|
+
const resolvedRoot = this.safeRealpath(this.rootDir);
|
|
213
|
+
if (resolvedPath && resolvedRoot && resolvedPath === resolvedRoot) {
|
|
214
|
+
console.warn(`[SECURITY] Detected main worktree via realpath (flag was false): ${wt.path}`);
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
});
|
|
125
219
|
for (const worktree of additionalWorktrees) {
|
|
126
220
|
try {
|
|
127
221
|
await this.removeWorktree(worktree.path);
|
|
@@ -160,6 +254,12 @@ export class WorktreeService {
|
|
|
160
254
|
results.failed.push({ name, error: 'Worktree not found' });
|
|
161
255
|
continue;
|
|
162
256
|
}
|
|
257
|
+
// SECURITY: Never delete main worktree
|
|
258
|
+
if (worktree.isMainWorktree) {
|
|
259
|
+
console.warn(`[SECURITY] Blocked attempt to delete main worktree by name: ${name}`);
|
|
260
|
+
results.failed.push({ name, error: 'Cannot delete main worktree' });
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
163
263
|
try {
|
|
164
264
|
await this.removeWorktree(worktree.path);
|
|
165
265
|
results.deleted.push(name);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktreeService.js","sourceRoot":"","sources":["../../../server/services/worktreeService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAClD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"worktreeService.js","sourceRoot":"","sources":["../../../server/services/worktreeService.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socketHandlers.d.ts","sourceRoot":"","sources":["../../server/socketHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"socketHandlers.d.ts","sourceRoot":"","sources":["../../server/socketHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,WAAW,CAAC;AAwG3C,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,QAqI1D;AAED,wBAAgB,eAAe,IAAI,IAAI,CAMtC"}
|
|
@@ -1,5 +1,66 @@
|
|
|
1
1
|
import * as pty from 'node-pty';
|
|
2
2
|
import { AI_AGENTS } from '../shared/constants.js';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import { promisify } from 'util';
|
|
5
|
+
const access = promisify(fs.access);
|
|
6
|
+
// Validation utilities
|
|
7
|
+
async function validatePath(pathToCheck) {
|
|
8
|
+
try {
|
|
9
|
+
await access(pathToCheck, fs.constants.R_OK | fs.constants.X_OK);
|
|
10
|
+
return { valid: true };
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
return {
|
|
14
|
+
valid: false,
|
|
15
|
+
error: error instanceof Error ? error.message : 'Path not accessible'
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function validateShell(shell) {
|
|
20
|
+
if (process.platform === 'win32') {
|
|
21
|
+
return { valid: true };
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
await access(shell, fs.constants.X_OK);
|
|
25
|
+
return { valid: true };
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return {
|
|
29
|
+
valid: false,
|
|
30
|
+
error: `Shell not executable: ${shell}`
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// PTY spawn with retry logic
|
|
35
|
+
async function spawnWithRetry(shell, args, options, maxRetries = 3) {
|
|
36
|
+
let lastError = null;
|
|
37
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
38
|
+
try {
|
|
39
|
+
const ptyProcess = pty.spawn(shell, args, options);
|
|
40
|
+
return ptyProcess;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
44
|
+
console.error(`[PTY] Spawn attempt ${attempt + 1}/${maxRetries} failed:`, lastError.message);
|
|
45
|
+
if (attempt < maxRetries - 1) {
|
|
46
|
+
const delay = Math.pow(2, attempt) * 100;
|
|
47
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
throw lastError || new Error('Failed to spawn PTY after retries');
|
|
52
|
+
}
|
|
53
|
+
// Spawn interval control to prevent resource contention
|
|
54
|
+
let lastSpawnTime = 0;
|
|
55
|
+
const MIN_SPAWN_INTERVAL = 150;
|
|
56
|
+
async function waitForSpawnInterval() {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
const elapsed = now - lastSpawnTime;
|
|
59
|
+
if (elapsed < MIN_SPAWN_INTERVAL) {
|
|
60
|
+
await new Promise(resolve => setTimeout(resolve, MIN_SPAWN_INTERVAL - elapsed));
|
|
61
|
+
}
|
|
62
|
+
lastSpawnTime = Date.now();
|
|
63
|
+
}
|
|
3
64
|
const sessions = new Map();
|
|
4
65
|
function getAgentCommand(agentType) {
|
|
5
66
|
const agent = AI_AGENTS.find(a => a.id === agentType);
|
|
@@ -18,7 +79,19 @@ export function setupSocketHandlers(io, cwd) {
|
|
|
18
79
|
try {
|
|
19
80
|
const shell = getShell();
|
|
20
81
|
const agentCommand = getAgentCommand(agentType);
|
|
21
|
-
|
|
82
|
+
// Validate path before spawning
|
|
83
|
+
const pathValidation = await validatePath(worktreePath);
|
|
84
|
+
if (!pathValidation.valid) {
|
|
85
|
+
throw new Error(`Invalid worktree path: ${pathValidation.error}`);
|
|
86
|
+
}
|
|
87
|
+
// Validate shell before spawning
|
|
88
|
+
const shellValidation = await validateShell(shell);
|
|
89
|
+
if (!shellValidation.valid) {
|
|
90
|
+
throw new Error(`Invalid shell: ${shellValidation.error}`);
|
|
91
|
+
}
|
|
92
|
+
// Wait for spawn interval to prevent resource contention
|
|
93
|
+
await waitForSpawnInterval();
|
|
94
|
+
const ptyProcess = await spawnWithRetry(shell, [], {
|
|
22
95
|
name: 'xterm-256color',
|
|
23
96
|
cols: 120,
|
|
24
97
|
rows: 30,
|