source dump of claude code
at main 127 lines 4.4 kB view raw
1import { z } from 'zod/v4' 2import { getSessionId, setOriginalCwd } from '../../bootstrap/state.js' 3import { clearSystemPromptSections } from '../../constants/systemPromptSections.js' 4import { logEvent } from '../../services/analytics/index.js' 5import type { Tool } from '../../Tool.js' 6import { buildTool, type ToolDef } from '../../Tool.js' 7import { clearMemoryFileCaches } from '../../utils/claudemd.js' 8import { getCwd } from '../../utils/cwd.js' 9import { findCanonicalGitRoot } from '../../utils/git.js' 10import { lazySchema } from '../../utils/lazySchema.js' 11import { getPlanSlug, getPlansDirectory } from '../../utils/plans.js' 12import { setCwd } from '../../utils/Shell.js' 13import { saveWorktreeState } from '../../utils/sessionStorage.js' 14import { 15 createWorktreeForSession, 16 getCurrentWorktreeSession, 17 validateWorktreeSlug, 18} from '../../utils/worktree.js' 19import { ENTER_WORKTREE_TOOL_NAME } from './constants.js' 20import { getEnterWorktreeToolPrompt } from './prompt.js' 21import { renderToolResultMessage, renderToolUseMessage } from './UI.js' 22 23const inputSchema = lazySchema(() => 24 z.strictObject({ 25 name: z 26 .string() 27 .superRefine((s, ctx) => { 28 try { 29 validateWorktreeSlug(s) 30 } catch (e) { 31 ctx.addIssue({ code: 'custom', message: (e as Error).message }) 32 } 33 }) 34 .optional() 35 .describe( 36 'Optional name for the worktree. Each "/"-separated segment may contain only letters, digits, dots, underscores, and dashes; max 64 chars total. A random name is generated if not provided.', 37 ), 38 }), 39) 40type InputSchema = ReturnType<typeof inputSchema> 41 42const outputSchema = lazySchema(() => 43 z.object({ 44 worktreePath: z.string(), 45 worktreeBranch: z.string().optional(), 46 message: z.string(), 47 }), 48) 49type OutputSchema = ReturnType<typeof outputSchema> 50export type Output = z.infer<OutputSchema> 51 52export const EnterWorktreeTool: Tool<InputSchema, Output> = buildTool({ 53 name: ENTER_WORKTREE_TOOL_NAME, 54 searchHint: 'create an isolated git worktree and switch into it', 55 maxResultSizeChars: 100_000, 56 async description() { 57 return 'Creates an isolated worktree (via git or configured hooks) and switches the session into it' 58 }, 59 async prompt() { 60 return getEnterWorktreeToolPrompt() 61 }, 62 get inputSchema(): InputSchema { 63 return inputSchema() 64 }, 65 get outputSchema(): OutputSchema { 66 return outputSchema() 67 }, 68 userFacingName() { 69 return 'Creating worktree' 70 }, 71 shouldDefer: true, 72 toAutoClassifierInput(input) { 73 return input.name ?? '' 74 }, 75 renderToolUseMessage, 76 renderToolResultMessage, 77 async call(input) { 78 // Validate not already in a worktree created by this session 79 if (getCurrentWorktreeSession()) { 80 throw new Error('Already in a worktree session') 81 } 82 83 // Resolve to main repo root so worktree creation works from within a worktree 84 const mainRepoRoot = findCanonicalGitRoot(getCwd()) 85 if (mainRepoRoot && mainRepoRoot !== getCwd()) { 86 process.chdir(mainRepoRoot) 87 setCwd(mainRepoRoot) 88 } 89 90 const slug = input.name ?? getPlanSlug() 91 92 const worktreeSession = await createWorktreeForSession(getSessionId(), slug) 93 94 process.chdir(worktreeSession.worktreePath) 95 setCwd(worktreeSession.worktreePath) 96 setOriginalCwd(getCwd()) 97 saveWorktreeState(worktreeSession) 98 // Clear cached system prompt sections so env_info_simple recomputes with worktree context 99 clearSystemPromptSections() 100 // Clear memoized caches that depend on CWD 101 clearMemoryFileCaches() 102 getPlansDirectory.cache.clear?.() 103 104 logEvent('tengu_worktree_created', { 105 mid_session: true, 106 }) 107 108 const branchInfo = worktreeSession.worktreeBranch 109 ? ` on branch ${worktreeSession.worktreeBranch}` 110 : '' 111 112 return { 113 data: { 114 worktreePath: worktreeSession.worktreePath, 115 worktreeBranch: worktreeSession.worktreeBranch, 116 message: `Created worktree at ${worktreeSession.worktreePath}${branchInfo}. The session is now working in the worktree. Use ExitWorktree to leave mid-session, or exit the session to be prompted.`, 117 }, 118 } 119 }, 120 mapToolResultToToolResultBlockParam({ message }, toolUseID) { 121 return { 122 type: 'tool_result', 123 content: message, 124 tool_use_id: toolUseID, 125 } 126 }, 127} satisfies ToolDef<InputSchema, Output>)