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,35 @@
1
+ import type { AgentType } from './types/index.js';
2
+
3
+ export const AI_AGENTS: AgentType[] = [
4
+ {
5
+ id: 'codex',
6
+ name: 'Codex CLI',
7
+ command: 'codex',
8
+ installCommand: 'npm install -g @openai/codex',
9
+ description: 'OpenAI Codex CLI - AI-powered coding assistant'
10
+ },
11
+ {
12
+ id: 'claude',
13
+ name: 'Claude Code',
14
+ command: 'claude',
15
+ installCommand: 'npm install -g @anthropic-ai/claude-code',
16
+ description: 'Anthropic Claude Code - Advanced AI coding assistant'
17
+ },
18
+ {
19
+ id: 'gemini',
20
+ name: 'Gemini CLI',
21
+ command: 'gemini',
22
+ installCommand: 'npm install -g @google/gemini-cli',
23
+ description: 'Google Gemini CLI - Multi-modal AI assistant'
24
+ }
25
+ ];
26
+
27
+ export const MAX_PARALLEL_COUNT = 10;
28
+ export const MIN_PARALLEL_COUNT = 1;
29
+ export const DEFAULT_PARALLEL_COUNT = 3;
30
+
31
+ export const BRANCH_POLL_INTERVAL = 5000; // 5 seconds
32
+
33
+ export const APP_NAME = 'hjWorktree CLI';
34
+ export const APP_VERSION = '2.0.0';
35
+ export const DEFAULT_PORT = 3847;
@@ -0,0 +1,92 @@
1
+ // Navigation step types (4-step wizard)
2
+ export type NavigationStep = 'branch' | 'agent' | 'worktree' | 'running';
3
+
4
+ // Step status for LNB display
5
+ export type StepStatus = 'pending' | 'current' | 'completed';
6
+
7
+ // Step configuration
8
+ export interface StepConfig {
9
+ id: NavigationStep;
10
+ number: number;
11
+ label: string;
12
+ description: string;
13
+ }
14
+
15
+ // Step order constant
16
+ export const STEP_ORDER: NavigationStep[] = ['branch', 'agent', 'worktree', 'running'];
17
+
18
+ // Agent types
19
+ export type AgentId = 'codex' | 'claude' | 'gemini';
20
+
21
+ export interface AgentType {
22
+ id: AgentId;
23
+ name: string;
24
+ command: string;
25
+ installCommand: string;
26
+ description: string;
27
+ }
28
+
29
+ // Branch types
30
+ export interface Branch {
31
+ name: string;
32
+ isCurrent: boolean;
33
+ isRemote: boolean;
34
+ lastCommit?: string;
35
+ }
36
+
37
+ // Running agent types
38
+ export type AgentStatus = 'initializing' | 'installing' | 'running' | 'stopped' | 'error';
39
+
40
+ export interface TerminalInfo {
41
+ sessionId: string;
42
+ worktreePath: string;
43
+ worktreeName: string;
44
+ branchName: string;
45
+ agentType: AgentId;
46
+ status: AgentStatus;
47
+ }
48
+
49
+ // Worktree types
50
+ export interface Worktree {
51
+ path: string;
52
+ branch: string;
53
+ name: string;
54
+ }
55
+
56
+ // Socket.IO event types
57
+ export interface TerminalCreateData {
58
+ sessionId: string;
59
+ worktreePath: string;
60
+ agentType: AgentId;
61
+ }
62
+
63
+ export interface TerminalOutputData {
64
+ sessionId: string;
65
+ data: string;
66
+ }
67
+
68
+ export interface TerminalInputData {
69
+ sessionId: string;
70
+ data: string;
71
+ }
72
+
73
+ export interface TerminalResizeData {
74
+ sessionId: string;
75
+ cols: number;
76
+ rows: number;
77
+ }
78
+
79
+ export interface TerminalKillData {
80
+ sessionId: string;
81
+ }
82
+
83
+ // API response types
84
+ export interface CreateWorktreesRequest {
85
+ branch: string;
86
+ count: number;
87
+ agentType: AgentId;
88
+ }
89
+
90
+ export interface CreateWorktreesResponse {
91
+ worktrees: Worktree[];
92
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022"],
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "resolveJsonModule": true,
12
+ "declaration": true,
13
+ "declarationMap": true,
14
+ "outDir": "./dist",
15
+ "sourceMap": true,
16
+ "isolatedModules": true
17
+ },
18
+ "include": ["shared/**/*", "server/**/*"],
19
+ "exclude": ["node_modules", "dist", "web", "src"]
20
+ }
package/web/index.html ADDED
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>hjWorktree CLI</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ <script type="module" src="/src/main.tsx"></script>
14
+ </body>
15
+ </html>
@@ -0,0 +1,65 @@
1
+ import React, { useEffect } from 'react';
2
+ import { useAppStore } from './stores/useAppStore.js';
3
+ import { useSocket } from './hooks/useSocket.js';
4
+ import Header from './components/Layout/Header.js';
5
+ import MainLayout from './components/Layout/MainLayout.js';
6
+ import BranchStep from './components/Steps/BranchStep.js';
7
+ import AgentStep from './components/Steps/AgentStep.js';
8
+ import WorktreeStep from './components/Steps/WorktreeStep.js';
9
+ import TerminalPanel from './components/Terminal/TerminalPanel.js';
10
+
11
+ function App() {
12
+ const { step, isLoading, loadingMessage, error } = useAppStore();
13
+ const { connected } = useSocket();
14
+
15
+ // Fetch initial data
16
+ useEffect(() => {
17
+ useAppStore.getState().fetchProjectInfo();
18
+ useAppStore.getState().fetchBranches();
19
+ }, []);
20
+
21
+ const renderStepContent = () => {
22
+ switch (step) {
23
+ case 'branch':
24
+ return <BranchStep />;
25
+ case 'agent':
26
+ return <AgentStep />;
27
+ case 'worktree':
28
+ return <WorktreeStep />;
29
+ case 'running':
30
+ return <TerminalPanel />;
31
+ default:
32
+ return <BranchStep />;
33
+ }
34
+ };
35
+
36
+ return (
37
+ <div className="app">
38
+ <Header />
39
+
40
+ <MainLayout>
41
+ {renderStepContent()}
42
+ </MainLayout>
43
+
44
+ {isLoading && (
45
+ <div className="loading-overlay">
46
+ <div className="loading-spinner" />
47
+ <p>{loadingMessage || 'Loading...'}</p>
48
+ </div>
49
+ )}
50
+
51
+ {error && (
52
+ <div className="error-banner">
53
+ {error}
54
+ </div>
55
+ )}
56
+
57
+ <div className="connection-status">
58
+ <span className={`dot ${connected ? 'connected' : 'disconnected'}`} />
59
+ {connected ? 'Connected' : 'Disconnected'}
60
+ </div>
61
+ </div>
62
+ );
63
+ }
64
+
65
+ export default App;
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+ import { useAppStore } from '../../stores/useAppStore';
3
+ import { APP_NAME, APP_VERSION } from '../../../../shared/constants';
4
+
5
+ function Header() {
6
+ const projectInfo = useAppStore((state) => state.projectInfo);
7
+
8
+ return (
9
+ <header className="header">
10
+ <h1>{APP_NAME}</h1>
11
+ <div className="project-info">
12
+ {projectInfo && (
13
+ <>
14
+ <span className="branch">
15
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
16
+ <path d="M11.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5zm-2.25.75a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.493 2.493 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25zM4.25 12a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5zM3.5 3.25a.75.75 0 1 1 1.5 0 .75.75 0 0 1-1.5 0z"/>
17
+ </svg>
18
+ {projectInfo.currentBranch || 'Unknown'}
19
+ </span>
20
+ <span>{projectInfo.cwd}</span>
21
+ </>
22
+ )}
23
+ <span>v{APP_VERSION}</span>
24
+ </div>
25
+ </header>
26
+ );
27
+ }
28
+
29
+ export default Header;
@@ -0,0 +1,67 @@
1
+ import React from 'react';
2
+ import { useAppStore } from '../../stores/useAppStore.js';
3
+ import type { NavigationStep, StepStatus } from '../../../../shared/types/index.js';
4
+
5
+ interface StepItemProps {
6
+ step: NavigationStep;
7
+ number: number;
8
+ label: string;
9
+ status: StepStatus;
10
+ onClick: () => void;
11
+ disabled: boolean;
12
+ }
13
+
14
+ const STEP_CONFIG = [
15
+ { id: 'branch' as const, number: 1, label: 'Branch Selection' },
16
+ { id: 'agent' as const, number: 2, label: 'Agent Selection' },
17
+ { id: 'worktree' as const, number: 3, label: 'Worktree Count' },
18
+ { id: 'running' as const, number: 4, label: 'Running' },
19
+ ];
20
+
21
+ function StepItem({ number, label, status, onClick, disabled }: StepItemProps) {
22
+ return (
23
+ <button
24
+ className={`lnb-step ${status}`}
25
+ onClick={onClick}
26
+ disabled={disabled}
27
+ type="button"
28
+ >
29
+ <span className="step-number">
30
+ {status === 'completed' ? '✓' : number}
31
+ </span>
32
+ <span className="step-label">{label}</span>
33
+ </button>
34
+ );
35
+ }
36
+
37
+ function LeftNavBar() {
38
+ const { step, goToStep, canNavigateTo, getStepStatus } = useAppStore();
39
+
40
+ // Don't show LNB on running step
41
+ if (step === 'running') {
42
+ return null;
43
+ }
44
+
45
+ return (
46
+ <nav className="left-nav-bar">
47
+ <div className="lnb-header">
48
+ <span>Setup Steps</span>
49
+ </div>
50
+ <div className="lnb-steps">
51
+ {STEP_CONFIG.map((config) => (
52
+ <StepItem
53
+ key={config.id}
54
+ step={config.id}
55
+ number={config.number}
56
+ label={config.label}
57
+ status={getStepStatus(config.id)}
58
+ onClick={() => goToStep(config.id)}
59
+ disabled={!canNavigateTo(config.id)}
60
+ />
61
+ ))}
62
+ </div>
63
+ </nav>
64
+ );
65
+ }
66
+
67
+ export default LeftNavBar;
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import LeftNavBar from './LeftNavBar.js';
3
+ import { useAppStore } from '../../stores/useAppStore.js';
4
+
5
+ interface MainLayoutProps {
6
+ children: React.ReactNode;
7
+ }
8
+
9
+ function MainLayout({ children }: MainLayoutProps) {
10
+ const step = useAppStore((state) => state.step);
11
+ const showLNB = step !== 'running';
12
+
13
+ return (
14
+ <div className="main-layout">
15
+ {showLNB && <LeftNavBar />}
16
+ <div className={`main-content-area ${showLNB ? 'with-lnb' : 'full-width'}`}>
17
+ {children}
18
+ </div>
19
+ </div>
20
+ );
21
+ }
22
+
23
+ export default MainLayout;
@@ -0,0 +1,71 @@
1
+ import React from 'react';
2
+ import { useAppStore } from '../../stores/useAppStore.js';
3
+
4
+ interface StepContainerProps {
5
+ children: React.ReactNode;
6
+ title: string;
7
+ description?: string;
8
+ showNavButtons?: boolean;
9
+ nextLabel?: string;
10
+ onNext?: () => void;
11
+ nextDisabled?: boolean;
12
+ }
13
+
14
+ function StepContainer({
15
+ children,
16
+ title,
17
+ description,
18
+ showNavButtons = true,
19
+ nextLabel = 'Next',
20
+ onNext,
21
+ nextDisabled = false,
22
+ }: StepContainerProps) {
23
+ const { step, goToNext, goToPrev, isStepComplete } = useAppStore();
24
+
25
+ const isFirstStep = step === 'branch';
26
+ const canProceed = isStepComplete(step);
27
+
28
+ const handleNext = () => {
29
+ if (onNext) {
30
+ onNext();
31
+ } else {
32
+ goToNext();
33
+ }
34
+ };
35
+
36
+ return (
37
+ <div className="step-container">
38
+ <div className="step-header">
39
+ <h2>{title}</h2>
40
+ {description && <p>{description}</p>}
41
+ </div>
42
+
43
+ <div className="step-content">
44
+ {children}
45
+ </div>
46
+
47
+ {showNavButtons && (
48
+ <div className="step-navigation">
49
+ <button
50
+ className="nav-button prev"
51
+ onClick={goToPrev}
52
+ disabled={isFirstStep}
53
+ type="button"
54
+ >
55
+ Previous
56
+ </button>
57
+ <button
58
+ className="nav-button next"
59
+ onClick={handleNext}
60
+ disabled={nextDisabled || !canProceed}
61
+ type="button"
62
+ >
63
+ {nextLabel}
64
+ </button>
65
+ </div>
66
+ )}
67
+ </div>
68
+ );
69
+ }
70
+
71
+ export default StepContainer;
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+ import { useAppStore } from '../../stores/useAppStore';
3
+ import { AI_AGENTS } from '../../../../shared/constants';
4
+
5
+ function AgentSelector() {
6
+ const { selectedAgent, setSelectedAgent } = useAppStore();
7
+
8
+ return (
9
+ <div className="setup-section">
10
+ <label>Select AI Agent</label>
11
+ <div className="agent-cards">
12
+ {AI_AGENTS.map((agent) => (
13
+ <div
14
+ key={agent.id}
15
+ className={`agent-card ${selectedAgent === agent.id ? 'selected' : ''}`}
16
+ onClick={() => setSelectedAgent(agent.id)}
17
+ >
18
+ <h4>{agent.name}</h4>
19
+ <p>{agent.description}</p>
20
+ </div>
21
+ ))}
22
+ </div>
23
+ </div>
24
+ );
25
+ }
26
+
27
+ export default AgentSelector;
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import { useAppStore } from '../../stores/useAppStore';
3
+
4
+ function BranchSelector() {
5
+ const { branches, selectedBranch, setSelectedBranch } = useAppStore();
6
+
7
+ return (
8
+ <div className="setup-section">
9
+ <label htmlFor="branch-select">Select Branch</label>
10
+ <select
11
+ id="branch-select"
12
+ value={selectedBranch || ''}
13
+ onChange={(e) => setSelectedBranch(e.target.value || null)}
14
+ >
15
+ <option value="">-- Select a branch --</option>
16
+ {branches.map((branch) => (
17
+ <option key={branch.name} value={branch.name}>
18
+ {branch.name}
19
+ {branch.isCurrent ? ' (current)' : ''}
20
+ {branch.isRemote ? ' (remote)' : ''}
21
+ </option>
22
+ ))}
23
+ </select>
24
+ </div>
25
+ );
26
+ }
27
+
28
+ export default BranchSelector;
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import { useAppStore } from '../../stores/useAppStore';
3
+ import BranchSelector from './BranchSelector';
4
+ import AgentSelector from './AgentSelector';
5
+ import WorktreeCountSelector from './WorktreeCountSelector';
6
+
7
+ function SetupPanel() {
8
+ const { selectedBranch, isLoading, execute } = useAppStore();
9
+
10
+ return (
11
+ <div className="setup-panel">
12
+ <div style={{ textAlign: 'center', marginBottom: '16px' }}>
13
+ <h2>Configure Parallel Agents</h2>
14
+ <p>Set up multiple AI coding agents in separate git worktrees</p>
15
+ </div>
16
+
17
+ <BranchSelector />
18
+ <AgentSelector />
19
+ <WorktreeCountSelector />
20
+
21
+ <button
22
+ className="execute-button"
23
+ onClick={execute}
24
+ disabled={!selectedBranch || isLoading}
25
+ >
26
+ {isLoading ? 'Creating...' : 'Execute'}
27
+ </button>
28
+ </div>
29
+ );
30
+ }
31
+
32
+ export default SetupPanel;
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+ import { useAppStore } from '../../stores/useAppStore';
3
+ import { MIN_PARALLEL_COUNT, MAX_PARALLEL_COUNT } from '../../../../shared/constants';
4
+
5
+ function WorktreeCountSelector() {
6
+ const { worktreeCount, incrementWorktreeCount, decrementWorktreeCount } = useAppStore();
7
+
8
+ return (
9
+ <div className="setup-section">
10
+ <label>Number of Worktrees</label>
11
+ <div className="counter">
12
+ <button
13
+ onClick={decrementWorktreeCount}
14
+ disabled={worktreeCount <= MIN_PARALLEL_COUNT}
15
+ >
16
+ -
17
+ </button>
18
+ <span>{worktreeCount}</span>
19
+ <button
20
+ onClick={incrementWorktreeCount}
21
+ disabled={worktreeCount >= MAX_PARALLEL_COUNT}
22
+ >
23
+ +
24
+ </button>
25
+ </div>
26
+ </div>
27
+ );
28
+ }
29
+
30
+ export default WorktreeCountSelector;
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import StepContainer from '../Layout/StepContainer.js';
3
+ import AgentSelector from '../Setup/AgentSelector.js';
4
+ import { useAppStore } from '../../stores/useAppStore.js';
5
+
6
+ function AgentStep() {
7
+ const selectedAgent = useAppStore((state) => state.selectedAgent);
8
+
9
+ return (
10
+ <StepContainer
11
+ title="Select AI Agent"
12
+ description="Choose which AI coding agent to run in each worktree"
13
+ nextDisabled={!selectedAgent}
14
+ >
15
+ <AgentSelector />
16
+ </StepContainer>
17
+ );
18
+ }
19
+
20
+ export default AgentStep;
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import StepContainer from '../Layout/StepContainer.js';
3
+ import BranchSelector from '../Setup/BranchSelector.js';
4
+ import { useAppStore } from '../../stores/useAppStore.js';
5
+
6
+ function BranchStep() {
7
+ const selectedBranch = useAppStore((state) => state.selectedBranch);
8
+
9
+ return (
10
+ <StepContainer
11
+ title="Select Branch"
12
+ description="Choose the Git branch to create worktrees from"
13
+ nextDisabled={!selectedBranch}
14
+ >
15
+ <BranchSelector />
16
+ </StepContainer>
17
+ );
18
+ }
19
+
20
+ export default BranchStep;
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+ import StepContainer from '../Layout/StepContainer.js';
3
+ import WorktreeCountSelector from '../Setup/WorktreeCountSelector.js';
4
+ import { useAppStore } from '../../stores/useAppStore.js';
5
+ import { AI_AGENTS } from '../../../../shared/constants.js';
6
+
7
+ function WorktreeStep() {
8
+ const { execute, isLoading, selectedBranch, selectedAgent, worktreeCount } = useAppStore();
9
+
10
+ const agentName = AI_AGENTS.find(a => a.id === selectedAgent)?.name || selectedAgent;
11
+
12
+ return (
13
+ <StepContainer
14
+ title="Configure Worktrees"
15
+ description="Set the number of parallel worktrees to create"
16
+ nextLabel={isLoading ? 'Creating...' : 'Execute'}
17
+ onNext={execute}
18
+ nextDisabled={!selectedBranch || isLoading || worktreeCount < 1}
19
+ >
20
+ <WorktreeCountSelector />
21
+
22
+ <div className="execution-summary">
23
+ <h4>Summary</h4>
24
+ <div className="summary-item">
25
+ <span className="summary-label">Branch:</span>
26
+ <span className="summary-value">{selectedBranch}</span>
27
+ </div>
28
+ <div className="summary-item">
29
+ <span className="summary-label">Agent:</span>
30
+ <span className="summary-value">{agentName}</span>
31
+ </div>
32
+ <div className="summary-item">
33
+ <span className="summary-label">Worktrees:</span>
34
+ <span className="summary-value">{worktreeCount}</span>
35
+ </div>
36
+ </div>
37
+ </StepContainer>
38
+ );
39
+ }
40
+
41
+ export default WorktreeStep;