feat: 工具层及 mcp 大重构 (#252)

* feat: 第一版大重构

* fix: 修复类型问题

* chore: 更新版本到 1.3.2

* Add brave as alternative WebSearchTool

* fix: 修正顺序

* fix: 修复对穷鬼模式的 auto dream 和 session memory 越过

* feat: 穷鬼模式去除 session-summary

* feat: 创建 builtin-tools 包,搬运所有工具实现

将 src/tools/ 下的全部 60 个工具目录迁移至 packages/builtin-tools/src/tools/,
内部导入路径已更新为 src/ alias 模式。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 更新 src/ 中所有工具引用至 builtin-tools 包,删除 src/tools/

- src/tools.ts 及 178 个 src/ 文件的 import 路径从 ./tools/ 改为 builtin-tools/tools/
- 删除 src/tools/ 整个目录(已迁移至 packages/builtin-tools/)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: 添加 builtin-tools 路径别名至 tsconfig,更新 bun.lock

- tsconfig.json 新增 builtin-tools/* 和 builtin-tools 路径映射
- 新增 packages/builtin-tools/src 至 include

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: 为 builtin-tools、mcp-client、agent-tools 添加 @claude-code-best 作用域前缀

所有包名及 import 路径统一添加 @claude-code-best/ 前缀:
- builtin-tools → @claude-code-best/builtin-tools
- mcp-client → @claude-code-best/mcp-client
- agent-tools → @claude-code-best/agent-tools

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: 修复 node 环境没有 bun 的问题

---------

Co-authored-by: Eric-Guo <eric.guocz@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-13 09:52:05 +08:00
committed by GitHub
parent bbb8b613a9
commit 2fb1c9dcd8
559 changed files with 9346 additions and 1837 deletions

View File

@@ -0,0 +1,127 @@
import { z } from 'zod/v4'
import { getSessionId, setOriginalCwd } from 'src/bootstrap/state.js'
import { clearSystemPromptSections } from 'src/constants/systemPromptSections.js'
import { logEvent } from 'src/services/analytics/index.js'
import type { Tool } from 'src/Tool.js'
import { buildTool, type ToolDef } from 'src/Tool.js'
import { clearMemoryFileCaches } from 'src/utils/claudemd.js'
import { getCwd } from 'src/utils/cwd.js'
import { findCanonicalGitRoot } from 'src/utils/git.js'
import { lazySchema } from 'src/utils/lazySchema.js'
import { getPlanSlug, getPlansDirectory } from 'src/utils/plans.js'
import { setCwd } from 'src/utils/Shell.js'
import { saveWorktreeState } from 'src/utils/sessionStorage.js'
import {
createWorktreeForSession,
getCurrentWorktreeSession,
validateWorktreeSlug,
} from 'src/utils/worktree.js'
import { ENTER_WORKTREE_TOOL_NAME } from './constants.js'
import { getEnterWorktreeToolPrompt } from './prompt.js'
import { renderToolResultMessage, renderToolUseMessage } from './UI.js'
const inputSchema = lazySchema(() =>
z.strictObject({
name: z
.string()
.superRefine((s, ctx) => {
try {
validateWorktreeSlug(s)
} catch (e) {
ctx.addIssue({ code: 'custom', message: (e as Error).message })
}
})
.optional()
.describe(
'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.',
),
}),
)
type InputSchema = ReturnType<typeof inputSchema>
const outputSchema = lazySchema(() =>
z.object({
worktreePath: z.string(),
worktreeBranch: z.string().optional(),
message: z.string(),
}),
)
type OutputSchema = ReturnType<typeof outputSchema>
export type Output = z.infer<OutputSchema>
export const EnterWorktreeTool: Tool<InputSchema, Output> = buildTool({
name: ENTER_WORKTREE_TOOL_NAME,
searchHint: 'create an isolated git worktree and switch into it',
maxResultSizeChars: 100_000,
async description() {
return 'Creates an isolated worktree (via git or configured hooks) and switches the session into it'
},
async prompt() {
return getEnterWorktreeToolPrompt()
},
get inputSchema(): InputSchema {
return inputSchema()
},
get outputSchema(): OutputSchema {
return outputSchema()
},
userFacingName() {
return 'Creating worktree'
},
shouldDefer: true,
toAutoClassifierInput(input) {
return input.name ?? ''
},
renderToolUseMessage,
renderToolResultMessage,
async call(input) {
// Validate not already in a worktree created by this session
if (getCurrentWorktreeSession()) {
throw new Error('Already in a worktree session')
}
// Resolve to main repo root so worktree creation works from within a worktree
const mainRepoRoot = findCanonicalGitRoot(getCwd())
if (mainRepoRoot && mainRepoRoot !== getCwd()) {
process.chdir(mainRepoRoot)
setCwd(mainRepoRoot)
}
const slug = input.name ?? getPlanSlug()
const worktreeSession = await createWorktreeForSession(getSessionId(), slug)
process.chdir(worktreeSession.worktreePath)
setCwd(worktreeSession.worktreePath)
setOriginalCwd(getCwd())
saveWorktreeState(worktreeSession)
// Clear cached system prompt sections so env_info_simple recomputes with worktree context
clearSystemPromptSections()
// Clear memoized caches that depend on CWD
clearMemoryFileCaches()
getPlansDirectory.cache.clear?.()
logEvent('tengu_worktree_created', {
mid_session: true,
})
const branchInfo = worktreeSession.worktreeBranch
? ` on branch ${worktreeSession.worktreeBranch}`
: ''
return {
data: {
worktreePath: worktreeSession.worktreePath,
worktreeBranch: worktreeSession.worktreeBranch,
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.`,
},
}
},
mapToolResultToToolResultBlockParam({ message }, toolUseID) {
return {
type: 'tool_result',
content: message,
tool_use_id: toolUseID,
}
},
} satisfies ToolDef<InputSchema, Output>)

View File

@@ -0,0 +1,25 @@
import * as React from 'react'
import { Box, Text } from '@anthropic/ink'
import type { ToolProgressData } from 'src/Tool.js'
import type { ProgressMessage } from 'src/types/message.js'
import type { ThemeName } from 'src/utils/theme.js'
import type { Output } from './EnterWorktreeTool.js'
export function renderToolUseMessage(): React.ReactNode {
return 'Creating worktree…'
}
export function renderToolResultMessage(
output: Output,
_progressMessagesForMessage: ProgressMessage<ToolProgressData>[],
_options: { theme: ThemeName },
): React.ReactNode {
return (
<Box flexDirection="column">
<Text>
Switched to worktree on branch <Text bold>{output.worktreeBranch}</Text>
</Text>
<Text dimColor>{output.worktreePath}</Text>
</Box>
)
}

View File

@@ -0,0 +1 @@
export const ENTER_WORKTREE_TOOL_NAME = 'EnterWorktree'

View File

@@ -0,0 +1,30 @@
export function getEnterWorktreeToolPrompt(): string {
return `Use this tool ONLY when the user explicitly asks to work in a worktree. This tool creates an isolated git worktree and switches the current session into it.
## When to Use
- The user explicitly says "worktree" (e.g., "start a worktree", "work in a worktree", "create a worktree", "use a worktree")
## When NOT to Use
- The user asks to create a branch, switch branches, or work on a different branch — use git commands instead
- The user asks to fix a bug or work on a feature — use normal git workflow unless they specifically mention worktrees
- Never use this tool unless the user explicitly mentions "worktree"
## Requirements
- Must be in a git repository, OR have WorktreeCreate/WorktreeRemove hooks configured in settings.json
- Must not already be in a worktree
## Behavior
- In a git repository: creates a new git worktree inside \`.claude/worktrees/\` with a new branch based on HEAD
- Outside a git repository: delegates to WorktreeCreate/WorktreeRemove hooks for VCS-agnostic isolation
- Switches the session's working directory to the new worktree
- Use ExitWorktree to leave the worktree mid-session (keep or remove). On session exit, if still in the worktree, the user will be prompted to keep or remove it
## Parameters
- \`name\` (optional): A name for the worktree. If not provided, a random name is generated.
`
}