feat: add git worktree session support with improved UI

Implement comprehensive worktree session support allowing users to spawn sessions in temporary git worktrees. Includes backend worktree management, full-stack integration, and refined UI for session type selection.

Backend:
- Add worktree creation/removal utilities with branch management
- Track worktree metadata (basePath, branch, name, path) in session metadata
- Automatic cleanup of worktrees when sessions fail or exit
- Enhanced error handling with stderr tail logging

UI improvements:
- Redesign session type toggle with improved alignment and spacing
- Move worktree description inline with label for cleaner layout
- Add branch name input field that appears when worktree mode selected
- Auto-focus on worktree input when switching modes
- Reduce gap between radio options from gap-3 to gap-1.5
- Update descriptive text and placeholders for clarity

Integration:
- Thread worktree parameters through API client, RPC handlers, and daemon
- Add worktreeEnv utility to read worktree info from environment
- Update session spawning to support both simple and worktree modes
This commit is contained in:
weishu
2025-12-28 13:17:58 +08:00
parent 0eab45c49c
commit c35a5ce827
21 changed files with 605 additions and 69 deletions
+6 -2
View File
@@ -6,7 +6,9 @@ import type { WebAppEnv } from '../middleware/auth'
const spawnBodySchema = z.object({
directory: z.string().min(1),
agent: z.enum(['claude', 'codex', 'gemini']).optional(),
yolo: z.boolean().optional()
yolo: z.boolean().optional(),
sessionType: z.enum(['simple', 'worktree']).optional(),
worktreeName: z.string().optional()
})
export function createMachinesRoutes(getSyncEngine: () => SyncEngine | null): Hono<WebAppEnv> {
@@ -44,7 +46,9 @@ export function createMachinesRoutes(getSyncEngine: () => SyncEngine | null): Ho
machineId,
parsed.data.directory,
parsed.data.agent,
parsed.data.yolo
parsed.data.yolo,
parsed.data.sessionType,
parsed.data.worktreeName
)
return c.json(result)
})
+9 -1
View File
@@ -9,6 +9,13 @@ type SessionSummaryMetadata = {
path: string
summary?: { text: string }
flavor?: string | null
worktree?: {
basePath: string
branch: string
name: string
worktreePath?: string
createdAt?: number
}
}
type SessionSummary = {
@@ -29,7 +36,8 @@ function toSessionSummary(session: Session): SessionSummary {
name: session.metadata.name,
path: session.metadata.path,
summary: session.metadata.summary ? { text: session.metadata.summary.text } : undefined,
flavor: session.metadata.flavor ?? null
flavor: session.metadata.flavor ?? null,
worktree: session.metadata.worktree
} : null
const todoProgress = session.todos?.length ? {