hjworktree-cli 2.0.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.
Files changed (68) hide show
  1. package/.context-snapshots/context-snapshot-20260106-110353.md +66 -0
  2. package/.context-snapshots/context-snapshot-20260106-110441.md +66 -0
  3. package/.context-snapshots/context-snapshot-20260106-220000.md +99 -0
  4. package/AGENTS.md +29 -0
  5. package/CLAUDE.md +88 -0
  6. package/bin/cli.js +85 -0
  7. package/dist/server/index.d.ts +6 -0
  8. package/dist/server/index.d.ts.map +1 -0
  9. package/dist/server/index.js +64 -0
  10. package/dist/server/index.js.map +1 -0
  11. package/dist/server/routes/api.d.ts +3 -0
  12. package/dist/server/routes/api.d.ts.map +1 -0
  13. package/dist/server/routes/api.js +101 -0
  14. package/dist/server/routes/api.js.map +1 -0
  15. package/dist/server/services/gitService.d.ts +13 -0
  16. package/dist/server/services/gitService.d.ts.map +1 -0
  17. package/dist/server/services/gitService.js +84 -0
  18. package/dist/server/services/gitService.js.map +1 -0
  19. package/dist/server/services/worktreeService.d.ts +17 -0
  20. package/dist/server/services/worktreeService.d.ts.map +1 -0
  21. package/dist/server/services/worktreeService.js +161 -0
  22. package/dist/server/services/worktreeService.js.map +1 -0
  23. package/dist/server/socketHandlers.d.ts +4 -0
  24. package/dist/server/socketHandlers.d.ts.map +1 -0
  25. package/dist/server/socketHandlers.js +118 -0
  26. package/dist/server/socketHandlers.js.map +1 -0
  27. package/dist/shared/constants.d.ts +10 -0
  28. package/dist/shared/constants.d.ts.map +1 -0
  29. package/dist/shared/constants.js +31 -0
  30. package/dist/shared/constants.js.map +1 -0
  31. package/dist/shared/types/index.d.ts +67 -0
  32. package/dist/shared/types/index.d.ts.map +1 -0
  33. package/dist/shared/types/index.js +3 -0
  34. package/dist/shared/types/index.js.map +1 -0
  35. package/dist/web/assets/index-C61yAbey.css +32 -0
  36. package/dist/web/assets/index-WEdVUKxb.js +53 -0
  37. package/dist/web/assets/index-WEdVUKxb.js.map +1 -0
  38. package/dist/web/index.html +16 -0
  39. package/package.json +63 -0
  40. package/server/index.ts +75 -0
  41. package/server/routes/api.ts +108 -0
  42. package/server/services/gitService.ts +91 -0
  43. package/server/services/worktreeService.ts +181 -0
  44. package/server/socketHandlers.ts +157 -0
  45. package/shared/constants.ts +35 -0
  46. package/shared/types/index.ts +92 -0
  47. package/tsconfig.json +20 -0
  48. package/web/index.html +15 -0
  49. package/web/src/App.tsx +65 -0
  50. package/web/src/components/Layout/Header.tsx +29 -0
  51. package/web/src/components/Layout/LeftNavBar.tsx +67 -0
  52. package/web/src/components/Layout/MainLayout.tsx +23 -0
  53. package/web/src/components/Layout/StepContainer.tsx +71 -0
  54. package/web/src/components/Setup/AgentSelector.tsx +27 -0
  55. package/web/src/components/Setup/BranchSelector.tsx +28 -0
  56. package/web/src/components/Setup/SetupPanel.tsx +32 -0
  57. package/web/src/components/Setup/WorktreeCountSelector.tsx +30 -0
  58. package/web/src/components/Steps/AgentStep.tsx +20 -0
  59. package/web/src/components/Steps/BranchStep.tsx +20 -0
  60. package/web/src/components/Steps/WorktreeStep.tsx +41 -0
  61. package/web/src/components/Terminal/TerminalPanel.tsx +113 -0
  62. package/web/src/components/Terminal/XTerminal.tsx +203 -0
  63. package/web/src/hooks/useSocket.ts +80 -0
  64. package/web/src/main.tsx +10 -0
  65. package/web/src/stores/useAppStore.ts +348 -0
  66. package/web/src/styles/global.css +695 -0
  67. package/web/tsconfig.json +23 -0
  68. package/web/vite.config.ts +32 -0
@@ -0,0 +1,66 @@
1
+ # Context Snapshot
2
+
3
+ ## Session Metadata
4
+ - **Created**: ${iso}
5
+ - **CLI**: Claude Code
6
+ - **Project**: /Users/hyungju-lee/workspace/work/new/utils/hjWorktree-cli
7
+
8
+ ## Conversation Summary
9
+
10
+ ### User Requests
11
+ - Investigate and fix `posix_spawnp failed` when starting agents after creating worktrees.
12
+ - Fix Codex CLI error: `The cursor position could not be read within a normal duration`.
13
+ - Allow returning to the branch selection screen after running agents.
14
+ - Generate `AGENTS.md` contributor guide (completed earlier in session).
15
+
16
+ ### Completed Tasks
17
+ 1. Added robust PTY spawn gating (serialization, cooldown, async backoff) and clearer spawn errors.
18
+ 2. Fixed macOS `node-pty` `spawn-helper` permissions: runtime chmod + `postinstall` script.
19
+ 3. Added DSR (cursor position) query handling in PTY output to respond to `ESC[6n`.
20
+ 4. Added “return to branches” action from running step via left arrow, with cleanup.
21
+ 5. Ran build and PTY smoke tests; verified spawn-helper fix.
22
+
23
+ ### Modified Files
24
+ - `src/services/ptyService.ts`
25
+ - `src/services/agentService.ts`
26
+ - `src/hooks/useAgentRunner.ts`
27
+ - `src/hooks/useKeyboardNav.ts`
28
+ - `src/components/Layout/Footer.tsx`
29
+ - `src/App.tsx`
30
+ - `package.json`
31
+ - `scripts/fix-node-pty-permissions.js`
32
+ - `AGENTS.md`
33
+
34
+ ## Technical Context
35
+
36
+ ### Key Concepts
37
+ - node-pty PTY spawning (macOS `spawn-helper` permissions)
38
+ - Ink `useInput` key handling for navigation
39
+ - ANSI DSR cursor query/response (`ESC[6n` / `ESC[<row>;<col>R`)
40
+ - Worktree lifecycle cleanup and UI step transitions
41
+
42
+ ### Important Changes
43
+ **src/services/ptyService.ts**
44
+ - Serialize PTY spawns with cooldown and async retry/backoff.
45
+ - Ensure cwd exists before spawn and provide richer spawn errors.
46
+ - Ensure `spawn-helper` executable on macOS (runtime check + chmod).
47
+ - Detect and answer DSR cursor position queries; buffer partial sequences.
48
+
49
+ **scripts/fix-node-pty-permissions.js**
50
+ - Postinstall helper that chmods `node-pty` `spawn-helper` under macOS.
51
+
52
+ **package.json**
53
+ - Added `postinstall` hook to run permission fix.
54
+
55
+ **src/hooks/useAgentRunner.ts / src/hooks/useKeyboardNav.ts / src/App.tsx / src/components/Layout/Footer.tsx**
56
+ - Added “return to branches” flow from running step (`←`), with cleanup and updated UI hints.
57
+
58
+ ### Issues Found / Resolved
59
+ - `posix_spawnp failed`: Root cause was non-executable `spawn-helper` on macOS; fixed via chmod + postinstall.
60
+ - Codex cursor position error: PTY now responds to DSR queries to avoid cursor-read timeouts.
61
+ - No path back to branches from running step: added `←` handler to return safely.
62
+
63
+ ## Next Steps
64
+ - Validate the full interactive flow: `npm run dev` → select branch/agent/parallel → run.
65
+ - Confirm Codex no longer shows cursor position error in the real terminal.
66
+ - If Codex still errors, capture full stderr (ideally with `RUST_BACKTRACE=1`) for diagnosis.
@@ -0,0 +1,66 @@
1
+ # Context Snapshot
2
+
3
+ ## Session Metadata
4
+ - **Created**: 2026-01-06T11:04:41+09:00
5
+ - **CLI**: Claude Code
6
+ - **Project**: /Users/hyungju-lee/workspace/work/new/utils/hjWorktree-cli
7
+
8
+ ## Conversation Summary
9
+
10
+ ### User Requests
11
+ - Investigate and fix `posix_spawnp failed` when starting agents after creating worktrees.
12
+ - Fix Codex CLI error: `The cursor position could not be read within a normal duration`.
13
+ - Allow returning to the branch selection screen after running agents.
14
+ - Generate `AGENTS.md` contributor guide (completed earlier in session).
15
+
16
+ ### Completed Tasks
17
+ 1. Added robust PTY spawn gating (serialization, cooldown, async backoff) and clearer spawn errors.
18
+ 2. Fixed macOS `node-pty` `spawn-helper` permissions: runtime chmod + `postinstall` script.
19
+ 3. Added DSR (cursor position) query handling in PTY output to respond to `ESC[6n`.
20
+ 4. Added “return to branches” action from running step via left arrow, with cleanup.
21
+ 5. Ran build and PTY smoke tests; verified spawn-helper fix.
22
+
23
+ ### Modified Files
24
+ - `src/services/ptyService.ts`
25
+ - `src/services/agentService.ts`
26
+ - `src/hooks/useAgentRunner.ts`
27
+ - `src/hooks/useKeyboardNav.ts`
28
+ - `src/components/Layout/Footer.tsx`
29
+ - `src/App.tsx`
30
+ - `package.json`
31
+ - `scripts/fix-node-pty-permissions.js`
32
+ - `AGENTS.md`
33
+
34
+ ## Technical Context
35
+
36
+ ### Key Concepts
37
+ - node-pty PTY spawning (macOS `spawn-helper` permissions)
38
+ - Ink `useInput` key handling for navigation
39
+ - ANSI DSR cursor query/response (`ESC[6n` / `ESC[<row>;<col>R`)
40
+ - Worktree lifecycle cleanup and UI step transitions
41
+
42
+ ### Important Changes
43
+ **src/services/ptyService.ts**
44
+ - Serialize PTY spawns with cooldown and async retry/backoff.
45
+ - Ensure cwd exists before spawn and provide richer spawn errors.
46
+ - Ensure `spawn-helper` executable on macOS (runtime check + chmod).
47
+ - Detect and answer DSR cursor position queries; buffer partial sequences.
48
+
49
+ **scripts/fix-node-pty-permissions.js**
50
+ - Postinstall helper that chmods `node-pty` `spawn-helper` under macOS.
51
+
52
+ **package.json**
53
+ - Added `postinstall` hook to run permission fix.
54
+
55
+ **src/hooks/useAgentRunner.ts / src/hooks/useKeyboardNav.ts / src/App.tsx / src/components/Layout/Footer.tsx**
56
+ - Added “return to branches” flow from running step (`←`), with cleanup and updated UI hints.
57
+
58
+ ### Issues Found / Resolved
59
+ - `posix_spawnp failed`: Root cause was non-executable `spawn-helper` on macOS; fixed via chmod + postinstall.
60
+ - Codex cursor position error: PTY now responds to DSR queries to avoid cursor-read timeouts.
61
+ - No path back to branches from running step: added `←` handler to return safely.
62
+
63
+ ## Next Steps
64
+ - Validate the full interactive flow: `npm run dev` → select branch/agent/parallel → run.
65
+ - Confirm Codex no longer shows cursor position error in the real terminal.
66
+ - If Codex still errors, capture full stderr (ideally with `RUST_BACKTRACE=1`) for diagnosis.
@@ -0,0 +1,99 @@
1
+ # Context Snapshot
2
+
3
+ ## Session Metadata
4
+ - **Created**: 2026-01-06T22:00:00+09:00
5
+ - **CLI**: Claude Code
6
+ - **Project**: /Users/hyungju-lee/workspace/work/new/utils/hjWorktree-cli
7
+
8
+ ## Conversation Summary
9
+
10
+ ### User Requests
11
+ 1. hjWorktree CLI 도구 개발 (git worktree를 활용한 병렬 AI 에이전트 실행)
12
+ 2. Worktree 삭제 버그 수정 (`cannot delete branch used by worktree` 에러)
13
+ 3. `d`/`D` 키로 worktree 삭제 기능 추가
14
+ 4. `posix_spawnp failed` 에러 수정 (macOS ARM64에서 PTY 스폰 실패)
15
+ 5. main-project-1, main-project-2, main-project-3 브랜치 삭제
16
+
17
+ ### Completed Tasks
18
+ 1. hjWorktree CLI 전체 구현 (Ink + node-pty + simple-git + Zustand)
19
+ 2. `git worktree prune` 명령으로 orphan worktree 참조 정리 버그 수정
20
+ 3. `d` 키 (현재 에이전트 삭제), `D` 키 (전체 삭제) 기능 추가
21
+ 4. PTY 스폰 재시도 로직 추가 (최대 3회, 지수 백오프)
22
+ 5. 에이전트 시작 사이 150ms 지연 추가
23
+ 6. 디버그 로그 정리
24
+ 7. 테스트 worktree 및 브랜치 삭제
25
+
26
+ ### Modified Files
27
+ - `src/services/ptyService.ts` - PTY 스폰 재시도 로직 추가
28
+ - `src/services/worktreeService.ts` - `git worktree prune` 추가
29
+ - `src/services/agentService.ts` - `stopAndRemoveWorktree`, `stopAllAndRemoveWorktrees` 메서드 추가
30
+ - `src/hooks/useAgentRunner.ts` - 에이전트 시작 지연 추가, 삭제 함수 추가
31
+ - `src/hooks/useKeyboardNav.ts` - `d`/`D` 키 바인딩 추가
32
+ - `src/components/Layout/Footer.tsx` - 삭제 키 힌트 추가
33
+
34
+ ## Technical Context
35
+
36
+ ### Key Concepts
37
+ - **Ink**: React 기반 터미널 UI 프레임워크
38
+ - **node-pty**: PTY (pseudo-terminal) 관리
39
+ - **simple-git**: Git 작업 래퍼
40
+ - **Zustand**: 상태 관리
41
+ - **Git Worktree**: 동일 저장소에서 병렬 작업 디렉토리 생성
42
+
43
+ ### Important Changes
44
+
45
+ **src/services/ptyService.ts**
46
+ ```typescript
47
+ // PTY 스폰 재시도 로직 (posix_spawnp 실패 방지)
48
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
49
+ try {
50
+ ptyProcess = pty.spawn(...);
51
+ break;
52
+ } catch (error) {
53
+ // 100ms, 200ms 지수 백오프 대기 후 재시도
54
+ }
55
+ }
56
+ ```
57
+
58
+ **src/hooks/useAgentRunner.ts**
59
+ ```typescript
60
+ // 에이전트 시작 사이 150ms 지연 (리소스 할당 시간 확보)
61
+ if (i > 0) {
62
+ await delay(150);
63
+ }
64
+ ```
65
+
66
+ **src/services/worktreeService.ts**
67
+ ```typescript
68
+ // Worktree 삭제 후 orphan 참조 정리
69
+ await this.git.raw(['worktree', 'prune']);
70
+ ```
71
+
72
+ ### Resolved Issues
73
+
74
+ | 이슈 | 원인 | 해결책 |
75
+ |-----|------|-------|
76
+ | `cannot delete branch used by worktree` | `git worktree remove` 실패 후 `.git/worktrees/` 참조 잔존 | `git worktree prune` 추가 |
77
+ | `posix_spawnp failed` | macOS ARM64에서 빠른 연속 PTY 스폰 시 리소스 할당 실패 | 재시도 로직 + 스폰 간 지연 |
78
+
79
+ ## Next Steps
80
+
81
+ ### 테스트 필요
82
+ - `npm run dev` 실행 후 3개 이상 에이전트 병렬 시작 테스트
83
+ - `d`/`D` 키로 worktree 삭제 기능 테스트
84
+
85
+ ### 키보드 바인딩 (running 단계)
86
+ | 키 | 기능 |
87
+ |---|---|
88
+ | `d` | 현재 선택된 에이전트 중지 + worktree 삭제 |
89
+ | `D` | 모든 에이전트 중지 + 모든 worktree 삭제 |
90
+ | `Enter` | 입력 모드 |
91
+ | `Ctrl+B` | 브로드캐스트 모드 |
92
+ | `q` | 종료 (worktree 유지) |
93
+
94
+ ### Git Worktree 삭제 순서
95
+ ```bash
96
+ git worktree remove <path> --force
97
+ git worktree prune
98
+ git branch -D <branch-name>
99
+ ```
package/AGENTS.md ADDED
@@ -0,0 +1,29 @@
1
+ # Repository Guidelines
2
+
3
+ ## Project Structure & Module Organization
4
+ - `src/` contains the TypeScript + Ink UI app. Key areas: `components/` (UI), `hooks/` (React hooks), `services/` (Git/agent/PTY logic), `stores/` (Zustand state), `utils/`, `constants/`, and `types/`.
5
+ - `src/App.tsx` wires the UI; `src/index.tsx` is the CLI entry that renders the Ink app.
6
+ - `bin/hjWorktree.js` is the executable shim configured in `package.json`.
7
+ - `dist/` is the compiled output from `tsc` (do not edit by hand).
8
+
9
+ ## Build, Test, and Development Commands
10
+ - `npm run dev` runs the CLI in development using `tsx` (fast TypeScript execution).
11
+ - `npm run build` compiles TypeScript to `dist/` via `tsc`.
12
+ - `npm run start` runs the compiled CLI from `dist/`.
13
+ - `npm run prepublishOnly` builds before publishing.
14
+
15
+ ## Coding Style & Naming Conventions
16
+ - TypeScript + React (Ink) with ES modules; keep local imports using `.js` extensions (for ESM output).
17
+ - Use 2-space indentation, semicolons, and single quotes, matching existing files.
18
+ - Naming patterns: React components in `PascalCase` under `src/components/`; hooks `useThing.ts`; services `*Service.ts`; Zustand store `useAppStore.ts`.
19
+
20
+ ## Testing Guidelines
21
+ - No automated test suite is configured yet. If you add tests, introduce a `test` script in `package.json` and document the framework and file pattern (for example, `*.test.ts`).
22
+
23
+ ## Commit & Pull Request Guidelines
24
+ - Recent history favors conventional-style prefixes (for example `feat:`, `docs:`, `rebrand:`). Some older commits are terse; new commits should be descriptive and use a clear prefix when possible.
25
+ - PRs should include a concise description, how you verified changes (commands run), and any user-visible CLI output changes (paste a short snippet when helpful).
26
+
27
+ ## Configuration Notes
28
+ - Requires Node.js `>=20` (see `package.json`).
29
+ - Keep `dist/` in sync by running `npm run build` before releases.
package/CLAUDE.md ADDED
@@ -0,0 +1,88 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ hjWorktree CLI is a web-based tool that uses git worktree to run multiple AI coding agents (Codex CLI, Claude Code, Gemini CLI) in parallel. Built with React, xterm.js, Socket.IO, node-pty, simple-git, and Zustand.
8
+
9
+ ## Usage
10
+
11
+ ```bash
12
+ npm i -g hjworktree-cli # Install globally
13
+ cd /path/to/project # Navigate to your project
14
+ hjWorktree # Start web UI (opens browser automatically)
15
+ ```
16
+
17
+ ## Commands
18
+
19
+ ```bash
20
+ npm run dev # Run development mode (server + web)
21
+ npm run dev:server # Run server only (tsx watch)
22
+ npm run dev:web # Run web dev server only (Vite)
23
+ npm run build # Build server + web for production
24
+ npm run start # Run production server
25
+ ```
26
+
27
+ ## Architecture
28
+
29
+ ### Directory Structure
30
+ ```
31
+ hjWorktree-cli/
32
+ ├── bin/cli.js # CLI entry point (starts server + opens browser)
33
+ ├── server/ # Backend (Express + Socket.IO)
34
+ │ ├── index.ts # Server entry point
35
+ │ ├── socketHandlers.ts # Terminal PTY via Socket.IO
36
+ │ ├── routes/api.ts # REST API (branches, worktrees)
37
+ │ └── services/ # Git and worktree services
38
+ ├── web/ # Frontend (React + xterm.js)
39
+ │ ├── src/App.tsx
40
+ │ ├── src/components/ # Setup and Terminal components
41
+ │ ├── src/hooks/ # useSocket hook
42
+ │ └── src/stores/ # Zustand state management
43
+ ├── shared/ # Shared types and constants
44
+ └── dist/ # Production build output
45
+ ```
46
+
47
+ ### Backend (server/)
48
+ - **index.ts** - Express server with Socket.IO for real-time terminal communication
49
+ - **socketHandlers.ts** - PTY session management (create, input, resize, kill)
50
+ - **routes/api.ts** - REST API for branches and worktrees CRUD
51
+ - **services/gitService.ts** - Git operations via simple-git
52
+ - **services/worktreeService.ts** - Worktree CRUD with rollback on failure
53
+
54
+ ### Frontend (web/)
55
+ - **components/Setup/** - Branch selector, agent selector, worktree count
56
+ - **components/Terminal/** - xterm.js terminal with FitAddon
57
+ - **hooks/useSocket.ts** - Socket.IO client singleton
58
+ - **stores/useAppStore.ts** - Zustand store for app state
59
+
60
+ ### Shared (shared/)
61
+ - **types/index.ts** - TypeScript types shared between frontend and backend
62
+ - **constants.ts** - AI agents configuration, port, app info
63
+
64
+ ## Key Technical Details
65
+
66
+ ### Terminal Communication
67
+ - xterm.js on frontend connects via Socket.IO to node-pty on backend
68
+ - Events: `terminal:create`, `terminal:input`, `terminal:output`, `terminal:resize`, `terminal:kill`
69
+ - ResizeObserver + FitAddon for responsive terminal sizing
70
+
71
+ ### Worktree Cleanup
72
+ When removing worktrees, always run `git worktree prune` after `git worktree remove --force` to clear orphan references in `.git/worktrees/`, otherwise branch deletion fails.
73
+
74
+ ### API Endpoints
75
+ | Method | Endpoint | Description |
76
+ |--------|----------|-------------|
77
+ | GET | /api/info | Project info (cwd, current branch) |
78
+ | GET | /api/branches | List all branches |
79
+ | POST | /api/worktrees | Create multiple worktrees |
80
+ | DELETE | /api/worktrees/:name | Delete single worktree |
81
+ | DELETE | /api/worktrees | Delete all worktrees |
82
+
83
+ ## Code Style
84
+
85
+ - TypeScript with ESM
86
+ - Import local files with `.js` extension (ESM output)
87
+ - 2-space indentation, semicolons, single quotes
88
+ - Components: PascalCase, Hooks: `useThing.ts`, Services: `*Service.ts`
package/bin/cli.js ADDED
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn, execSync } from 'child_process';
4
+ import { fileURLToPath } from 'url';
5
+ import path from 'path';
6
+ import fs from 'fs';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ const PORT = process.env.PORT || 3847;
12
+ const CWD = process.cwd();
13
+
14
+ // Check if it's a git repository
15
+ try {
16
+ execSync('git status', { cwd: CWD, stdio: 'ignore' });
17
+ } catch {
18
+ console.error('Error: Current directory is not a git repository.');
19
+ console.error('Please run hjWorktree from within a git repository.');
20
+ process.exit(1);
21
+ }
22
+
23
+ // Find the server entry point
24
+ let serverPath = path.join(__dirname, '../dist/server/index.js');
25
+
26
+ // In development, use tsx with the source file
27
+ if (!fs.existsSync(serverPath)) {
28
+ serverPath = path.join(__dirname, '../server/index.ts');
29
+
30
+ if (!fs.existsSync(serverPath)) {
31
+ console.error('Error: Server entry point not found.');
32
+ console.error('Please run "npm run build" first.');
33
+ process.exit(1);
34
+ }
35
+ }
36
+
37
+ console.log(`Starting hjWorktree CLI...`);
38
+ console.log(`Working directory: ${CWD}`);
39
+ console.log(`Server port: ${PORT}`);
40
+
41
+ // Start the server
42
+ const serverArgs = serverPath.endsWith('.ts')
43
+ ? ['--import', 'tsx', serverPath]
44
+ : [serverPath];
45
+
46
+ const server = spawn('node', serverArgs, {
47
+ env: {
48
+ ...process.env,
49
+ PORT: String(PORT),
50
+ CWD: CWD,
51
+ },
52
+ stdio: 'inherit',
53
+ });
54
+
55
+ // Open browser after a delay
56
+ setTimeout(async () => {
57
+ try {
58
+ const open = (await import('open')).default;
59
+ await open(`http://localhost:${PORT}`);
60
+ console.log(`\nBrowser opened at http://localhost:${PORT}`);
61
+ } catch (error) {
62
+ console.log(`\nOpen http://localhost:${PORT} in your browser.`);
63
+ }
64
+ }, 1500);
65
+
66
+ // Handle process signals
67
+ process.on('SIGINT', () => {
68
+ console.log('\nShutting down...');
69
+ server.kill('SIGINT');
70
+ process.exit(0);
71
+ });
72
+
73
+ process.on('SIGTERM', () => {
74
+ server.kill('SIGTERM');
75
+ process.exit(0);
76
+ });
77
+
78
+ server.on('error', (error) => {
79
+ console.error('Server error:', error);
80
+ process.exit(1);
81
+ });
82
+
83
+ server.on('exit', (code) => {
84
+ process.exit(code || 0);
85
+ });
@@ -0,0 +1,6 @@
1
+ import { Server } from 'socket.io';
2
+ declare const app: import("express-serve-static-core").Express;
3
+ declare const httpServer: import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
4
+ declare const io: Server<import("socket.io").DefaultEventsMap, import("socket.io").DefaultEventsMap, import("socket.io").DefaultEventsMap, any>;
5
+ export { app, httpServer, io };
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAWnC,QAAA,MAAM,GAAG,6CAAY,CAAC;AACtB,QAAA,MAAM,UAAU,oGAAoB,CAAC;AACrC,QAAA,MAAM,EAAE,+HAKN,CAAC;AAsDH,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC"}
@@ -0,0 +1,64 @@
1
+ import express from 'express';
2
+ import { createServer } from 'http';
3
+ import { Server } from 'socket.io';
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import cors from 'cors';
7
+ import { setupSocketHandlers, killAllSessions } from './socketHandlers.js';
8
+ import { apiRouter } from './routes/api.js';
9
+ import { DEFAULT_PORT, APP_NAME, APP_VERSION } from '../shared/constants.js';
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = path.dirname(__filename);
12
+ const app = express();
13
+ const httpServer = createServer(app);
14
+ const io = new Server(httpServer, {
15
+ cors: {
16
+ origin: '*',
17
+ methods: ['GET', 'POST']
18
+ }
19
+ });
20
+ const PORT = parseInt(process.env.PORT || String(DEFAULT_PORT), 10);
21
+ const CWD = process.env.CWD || process.cwd();
22
+ // Middleware
23
+ app.use(cors());
24
+ app.use(express.json());
25
+ // Serve static files from the web build directory
26
+ const webDir = path.join(__dirname, '../web');
27
+ app.use(express.static(webDir));
28
+ // API routes
29
+ app.use('/api', apiRouter(CWD));
30
+ // Socket.IO handlers
31
+ setupSocketHandlers(io, CWD);
32
+ // Fallback to index.html for SPA routing
33
+ app.get('*', (req, res) => {
34
+ res.sendFile(path.join(webDir, 'index.html'));
35
+ });
36
+ // Graceful shutdown
37
+ process.on('SIGINT', () => {
38
+ console.log('\nShutting down...');
39
+ killAllSessions();
40
+ process.exit(0);
41
+ });
42
+ process.on('SIGTERM', () => {
43
+ console.log('\nShutting down...');
44
+ killAllSessions();
45
+ process.exit(0);
46
+ });
47
+ // Start server
48
+ httpServer.listen(PORT, () => {
49
+ console.log(`
50
+ ╔════════════════════════════════════════════╗
51
+ ║ ║
52
+ ║ ${APP_NAME} v${APP_VERSION} ║
53
+ ║ ║
54
+ ║ Server running at: ║
55
+ ║ http://localhost:${PORT} ║
56
+ ║ ║
57
+ ║ Working directory: ║
58
+ ║ ${CWD.substring(0, 38)}${CWD.length > 38 ? '...' : ''}
59
+ ║ ║
60
+ ╚════════════════════════════════════════════╝
61
+ `);
62
+ });
63
+ export { app, httpServer, io };
64
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Router } from 'express';
2
+ export declare function apiRouter(cwd: string): Router;
3
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +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,CAsG7C"}
@@ -0,0 +1,101 @@
1
+ import { Router } from 'express';
2
+ import { GitService } from '../services/gitService.js';
3
+ import { WorktreeService } from '../services/worktreeService.js';
4
+ export function apiRouter(cwd) {
5
+ const router = Router();
6
+ const gitService = new GitService(cwd);
7
+ const worktreeService = new WorktreeService(cwd);
8
+ // Get project info
9
+ router.get('/info', async (req, res) => {
10
+ try {
11
+ const isGitRepo = await gitService.isGitRepository();
12
+ const currentBranch = isGitRepo ? await gitService.getCurrentBranch() : null;
13
+ res.json({
14
+ cwd,
15
+ isGitRepository: isGitRepo,
16
+ currentBranch,
17
+ });
18
+ }
19
+ catch (error) {
20
+ res.status(500).json({
21
+ error: error instanceof Error ? error.message : 'Unknown error'
22
+ });
23
+ }
24
+ });
25
+ // Get branches
26
+ router.get('/branches', async (req, res) => {
27
+ try {
28
+ // Fetch latest from remote first
29
+ await gitService.fetch();
30
+ const branches = await gitService.getBranches();
31
+ res.json(branches);
32
+ }
33
+ catch (error) {
34
+ res.status(500).json({
35
+ error: error instanceof Error ? error.message : 'Unknown error'
36
+ });
37
+ }
38
+ });
39
+ // Get worktrees
40
+ router.get('/worktrees', async (req, res) => {
41
+ try {
42
+ const worktrees = await worktreeService.listWorktrees();
43
+ res.json(worktrees);
44
+ }
45
+ catch (error) {
46
+ res.status(500).json({
47
+ error: error instanceof Error ? error.message : 'Unknown error'
48
+ });
49
+ }
50
+ });
51
+ // Create worktrees
52
+ router.post('/worktrees', async (req, res) => {
53
+ try {
54
+ const { branch, count } = req.body;
55
+ if (!branch || !count || count < 1) {
56
+ res.status(400).json({ error: 'Invalid request: branch and count are required' });
57
+ return;
58
+ }
59
+ const worktrees = await worktreeService.createMultipleWorktrees(branch, count);
60
+ res.json({ worktrees });
61
+ }
62
+ catch (error) {
63
+ res.status(500).json({
64
+ error: error instanceof Error ? error.message : 'Unknown error'
65
+ });
66
+ }
67
+ });
68
+ // Delete a worktree
69
+ router.delete('/worktrees/:name', async (req, res) => {
70
+ try {
71
+ const { name } = req.params;
72
+ const worktrees = await worktreeService.listWorktrees();
73
+ const worktree = worktrees.find(wt => wt.name === name);
74
+ if (!worktree) {
75
+ res.status(404).json({ error: 'Worktree not found' });
76
+ return;
77
+ }
78
+ await worktreeService.removeWorktree(worktree.path);
79
+ res.json({ success: true });
80
+ }
81
+ catch (error) {
82
+ res.status(500).json({
83
+ error: error instanceof Error ? error.message : 'Unknown error'
84
+ });
85
+ }
86
+ });
87
+ // Delete all worktrees
88
+ router.delete('/worktrees', async (req, res) => {
89
+ try {
90
+ await worktreeService.removeAllWorktrees();
91
+ res.json({ success: true });
92
+ }
93
+ catch (error) {
94
+ res.status(500).json({
95
+ error: error instanceof Error ? error.message : 'Unknown error'
96
+ });
97
+ }
98
+ });
99
+ return router;
100
+ }
101
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../server/routes/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Branch } from '../../shared/types/index.js';
2
+ export declare class GitService {
3
+ private git;
4
+ private cwd;
5
+ constructor(cwd?: string);
6
+ isGitRepository(): Promise<boolean>;
7
+ getBranches(): Promise<Branch[]>;
8
+ getCurrentBranch(): Promise<string>;
9
+ fetch(): Promise<void>;
10
+ getCwd(): string;
11
+ }
12
+ export declare function getGitService(cwd?: string): GitService;
13
+ //# sourceMappingURL=gitService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitService.d.ts","sourceRoot":"","sources":["../../../server/services/gitService.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AAG1D,qBAAa,UAAU;IACrB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,GAAG,CAAS;gBAER,GAAG,GAAE,MAAsB;IAKjC,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IASnC,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IA0ChC,gBAAgB,IAAI,OAAO,CAAC,MAAM,CAAC;IAKnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B,MAAM,IAAI,MAAM;CAGjB;AAKD,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,UAAU,CAKtD"}