Skip to content

Commit 744ff6c

Browse files
Merge branch 'add-git-commit-button-in-task-hc5c'
2 parents 31f9021 + 5816405 commit 744ff6c

5 files changed

Lines changed: 165 additions & 247 deletions

File tree

src/components/terminal/git-actions-buttons.tsx

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Button } from '@/components/ui/button'
22
import { HugeiconsIcon } from '@hugeicons/react'
3-
import { ArrowDown03Icon, ArrowUp03Icon, Orbit01Icon, Menu01Icon } from '@hugeicons/core-free-icons'
3+
import { ArrowDown03Icon, ArrowUp03Icon, Orbit01Icon, Menu01Icon, GitCommitIcon } from '@hugeicons/core-free-icons'
44
import {
55
DropdownMenu,
66
DropdownMenuTrigger,
@@ -20,6 +20,8 @@ interface GitActionsButtonsProps {
2020
baseBranch: string
2121
taskId: string
2222
isMobile?: boolean
23+
terminalId?: string
24+
sendInputToTerminal?: (terminalId: string, text: string) => void
2325
}
2426

2527
export function GitActionsButtons({
@@ -28,13 +30,24 @@ export function GitActionsButtons({
2830
baseBranch,
2931
taskId,
3032
isMobile,
33+
terminalId,
34+
sendInputToTerminal,
3135
}: GitActionsButtonsProps) {
3236
const gitSync = useGitSync()
3337
const gitMerge = useGitMergeToMain()
3438
const gitSyncParent = useGitSyncParent()
3539
const updateTask = useUpdateTask()
3640
const killClaude = useKillClaudeInTask()
3741

42+
const resolveWithClaude = (prompt: string) => {
43+
if (terminalId && sendInputToTerminal) {
44+
sendInputToTerminal(terminalId, prompt)
45+
toast.info('Sent to Claude Code')
46+
} else {
47+
toast.error('No terminal available')
48+
}
49+
}
50+
3851
const handleSync = async () => {
3952
try {
4053
await gitSync.mutateAsync({
@@ -45,7 +58,15 @@ export function GitActionsButtons({
4558
toast.success('Synced from main')
4659
} catch (err) {
4760
const errorMessage = err instanceof Error ? err.message : 'Sync failed'
48-
toast.error(errorMessage)
61+
const branch = baseBranch || 'main'
62+
toast.error(errorMessage, {
63+
action: terminalId && sendInputToTerminal ? {
64+
label: 'Resolve with Claude',
65+
onClick: () => resolveWithClaude(
66+
`Rebase this worktree onto the parent repo's ${branch} branch. Error: "${errorMessage}". Steps: 1) Check for uncommitted changes - stash or commit them first, 2) git fetch origin (in parent repo at ${repoPath}) to ensure ${branch} is current, 3) git rebase ${branch} (in worktree), 4) Resolve any conflicts carefully - do not lose functionality or introduce regressions, 5) If stashed, git stash pop. Worktree: ${worktreePath}, Parent repo: ${repoPath}.`
67+
),
68+
} : undefined,
69+
})
4970
}
5071
}
5172

@@ -66,7 +87,15 @@ export function GitActionsButtons({
6687
})
6788
} catch (err) {
6889
const errorMessage = err instanceof Error ? err.message : 'Merge failed'
69-
toast.error(errorMessage)
90+
const branch = baseBranch || 'main'
91+
toast.error(errorMessage, {
92+
action: terminalId && sendInputToTerminal ? {
93+
label: 'Resolve with Claude',
94+
onClick: () => resolveWithClaude(
95+
`Merge this worktree's branch into the parent repo's ${branch}. Error: "${errorMessage}". Steps: 1) Ensure all changes in worktree are committed, 2) In parent repo at ${repoPath}, checkout ${branch} and pull latest from origin, 3) Merge the worktree branch into ${branch}, 4) Resolve any conflicts carefully - do not lose functionality or introduce regressions, 5) Push ${branch} to origin. Worktree: ${worktreePath}, Parent repo: ${repoPath}.`
96+
),
97+
} : undefined,
98+
})
7099
}
71100
}
72101

@@ -79,7 +108,21 @@ export function GitActionsButtons({
79108
toast.success('Parent synced with origin')
80109
} catch (err) {
81110
const errorMessage = err instanceof Error ? err.message : 'Sync parent failed'
82-
toast.error(errorMessage)
111+
const branch = baseBranch || 'main'
112+
toast.error(errorMessage, {
113+
action: terminalId && sendInputToTerminal ? {
114+
label: 'Resolve with Claude',
115+
onClick: () => resolveWithClaude(
116+
`Sync the parent repo's ${branch} branch with origin. Error: "${errorMessage}". Steps: 1) git fetch origin, 2) git pull origin ${branch} --ff-only, 3) If that fails, rebase with git rebase origin/${branch}, 4) Resolve any conflicts carefully - do not lose functionality or introduce regressions, 5) Once in sync, git push origin ${branch}. Work in the parent repo at ${repoPath}, not the worktree.`
117+
),
118+
} : undefined,
119+
})
120+
}
121+
}
122+
123+
const handleCommit = () => {
124+
if (terminalId && sendInputToTerminal) {
125+
sendInputToTerminal(terminalId, 'commit')
83126
}
84127
}
85128

@@ -126,6 +169,16 @@ export function GitActionsButtons({
126169
/>
127170
Sync parent with origin
128171
</DropdownMenuItem>
172+
{terminalId && sendInputToTerminal && (
173+
<DropdownMenuItem onClick={handleCommit}>
174+
<HugeiconsIcon
175+
icon={GitCommitIcon}
176+
size={12}
177+
strokeWidth={2}
178+
/>
179+
Commit
180+
</DropdownMenuItem>
181+
)}
129182
</DropdownMenuContent>
130183
</DropdownMenu>
131184
)
@@ -180,6 +233,22 @@ export function GitActionsButtons({
180233
className={gitSyncParent.isPending ? 'animate-spin' : ''}
181234
/>
182235
</Button>
236+
237+
{terminalId && sendInputToTerminal && (
238+
<Button
239+
variant="ghost"
240+
size="icon-xs"
241+
onClick={handleCommit}
242+
className="h-5 w-5 text-muted-foreground hover:text-foreground"
243+
title="Commit"
244+
>
245+
<HugeiconsIcon
246+
icon={GitCommitIcon}
247+
size={12}
248+
strokeWidth={2}
249+
/>
250+
</Button>
251+
)}
183252
</>
184253
)
185254
}

src/components/terminal/terminal-grid.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ interface TerminalGridProps {
3737
onTerminalContainerReady?: (terminalId: string, container: HTMLDivElement) => void
3838
setupImagePaste?: (container: HTMLElement, terminalId: string) => () => void
3939
writeToTerminal?: (terminalId: string, data: string) => void
40+
sendInputToTerminal?: (terminalId: string, text: string) => void
4041
/** Map terminal cwd to task info for navigation and display */
4142
taskInfoByCwd?: Map<string, TaskInfo>
4243
}
@@ -54,7 +55,7 @@ interface TerminalPaneProps {
5455
onFocus?: () => void
5556
}
5657

57-
function TerminalPane({ terminal, taskInfo, isMobile, onClose, onReady, onResize, onRename, onContainerReady, setupImagePaste, onFocus }: TerminalPaneProps) {
58+
function TerminalPane({ terminal, taskInfo, isMobile, onClose, onReady, onResize, onRename, onContainerReady, setupImagePaste, onFocus, sendInputToTerminal }: TerminalPaneProps & { sendInputToTerminal?: (terminalId: string, text: string) => void }) {
5859
return (
5960
<div className="flex h-full min-w-0 flex-col overflow-hidden">
6061
<div className="flex shrink-0 items-center justify-between border-b border-border bg-card">
@@ -90,6 +91,8 @@ function TerminalPane({ terminal, taskInfo, isMobile, onClose, onReady, onResize
9091
baseBranch={taskInfo.baseBranch}
9192
taskId={taskInfo.taskId}
9293
isMobile={isMobile}
94+
terminalId={terminal.id}
95+
sendInputToTerminal={sendInputToTerminal}
9396
/>
9497
</div>
9598
</div>
@@ -157,6 +160,7 @@ export function TerminalGrid({
157160
onTerminalContainerReady,
158161
setupImagePaste,
159162
writeToTerminal,
163+
sendInputToTerminal,
160164
taskInfoByCwd,
161165
}: TerminalGridProps) {
162166
const isMobile = useIsMobile()
@@ -198,6 +202,7 @@ export function TerminalGrid({
198202
onContainerReady={onTerminalContainerReady ? (container) => onTerminalContainerReady(terminal.id, container) : undefined}
199203
setupImagePaste={setupImagePaste}
200204
onFocus={() => setFocusedTerminalId(terminal.id)}
205+
sendInputToTerminal={sendInputToTerminal}
201206
/>
202207
)
203208

src/hooks/use-terminal-ws.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ interface UseTerminalWSReturn {
7070
createTerminal: (options: CreateTerminalOptions) => void
7171
destroyTerminal: (terminalId: string) => void
7272
writeToTerminal: (terminalId: string, data: string) => void
73+
sendInputToTerminal: (terminalId: string, text: string) => void
7374
resizeTerminal: (terminalId: string, cols: number, rows: number) => void
7475
renameTerminal: (terminalId: string, name: string) => void
7576
clearTerminalBuffer: (terminalId: string) => void
@@ -354,6 +355,25 @@ export function useTerminalWS(options: UseTerminalWSOptions = {}): UseTerminalWS
354355
[send]
355356
)
356357

358+
// Send text input followed by Enter key to terminal (for CLI tools like Claude Code)
359+
const sendInputToTerminal = useCallback(
360+
(terminalId: string, text: string) => {
361+
// Write the text first
362+
send({
363+
type: 'terminal:input',
364+
payload: { terminalId, data: text },
365+
})
366+
// Then send Enter (\r) after a brief delay to ensure text is processed first
367+
setTimeout(() => {
368+
send({
369+
type: 'terminal:input',
370+
payload: { terminalId, data: '\r' },
371+
})
372+
}, 50)
373+
},
374+
[send]
375+
)
376+
357377
const resizeTerminal = useCallback(
358378
(terminalId: string, cols: number, rows: number) => {
359379
send({
@@ -529,6 +549,7 @@ export function useTerminalWS(options: UseTerminalWSOptions = {}): UseTerminalWS
529549
createTerminal,
530550
destroyTerminal,
531551
writeToTerminal,
552+
sendInputToTerminal,
532553
resizeTerminal,
533554
renameTerminal,
534555
clearTerminalBuffer,

0 commit comments

Comments
 (0)