Files
claude-code/src/utils/queryContext.ts
claude-code-best 2fb1c9dcd8 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>
2026-04-13 09:52:05 +08:00

180 lines
5.8 KiB
TypeScript

/**
* Shared helpers for building the API cache-key prefix (systemPrompt,
* userContext, systemContext) for query() calls.
*
* Lives in its own file because it imports from context.ts and
* constants/prompts.ts, which are high in the dependency graph. Putting
* these imports in systemPrompt.ts or sideQuestion.ts (both reachable
* from commands.ts) would create cycles. Only entrypoint-layer files
* import from here (QueryEngine.ts, cli/print.ts).
*/
import type { Command } from '../commands.js'
import { getSystemPrompt } from '../constants/prompts.js'
import { getSystemContext, getUserContext } from '../context.js'
import type { MCPServerConnection } from '../services/mcp/types.js'
import type { AppState } from '../state/AppStateStore.js'
import type { Tools, ToolUseContext } from '../Tool.js'
import type { AgentDefinition } from '@claude-code-best/builtin-tools/tools/AgentTool/loadAgentsDir.js'
import type { Message } from '../types/message.js'
import { createAbortController } from './abortController.js'
import type { FileStateCache } from './fileStateCache.js'
import type { CacheSafeParams } from './forkedAgent.js'
import { getMainLoopModel } from './model/model.js'
import { asSystemPrompt } from './systemPromptType.js'
import {
shouldEnableThinkingByDefault,
type ThinkingConfig,
} from './thinking.js'
/**
* Fetch the three context pieces that form the API cache-key prefix:
* systemPrompt parts, userContext, systemContext.
*
* When customSystemPrompt is set, the default getSystemPrompt build and
* getSystemContext are skipped — the custom prompt replaces the default
* entirely, and systemContext would be appended to a default that isn't
* being used.
*
* Callers assemble the final systemPrompt from defaultSystemPrompt (or
* customSystemPrompt) + optional extras + appendSystemPrompt. QueryEngine
* injects coordinator userContext and memory-mechanics prompt on top;
* sideQuestion's fallback uses the base result directly.
*/
export async function fetchSystemPromptParts({
tools,
mainLoopModel,
additionalWorkingDirectories,
mcpClients,
customSystemPrompt,
}: {
tools: Tools
mainLoopModel: string
additionalWorkingDirectories: string[]
mcpClients: MCPServerConnection[]
customSystemPrompt: string | undefined
}): Promise<{
defaultSystemPrompt: string[]
userContext: { [k: string]: string }
systemContext: { [k: string]: string }
}> {
const [defaultSystemPrompt, userContext, systemContext] = await Promise.all([
customSystemPrompt !== undefined
? Promise.resolve([])
: getSystemPrompt(
tools,
mainLoopModel,
additionalWorkingDirectories,
mcpClients,
),
getUserContext(),
customSystemPrompt !== undefined ? Promise.resolve({}) : getSystemContext(),
])
return { defaultSystemPrompt, userContext, systemContext }
}
/**
* Build CacheSafeParams from raw inputs when getLastCacheSafeParams() is null.
*
* Used by the SDK side_question handler (print.ts) on resume before a turn
* completes — there's no stopHooks snapshot yet. Mirrors the system prompt
* assembly in QueryEngine.ts:ask() so the rebuilt prefix matches what the
* main loop will send, preserving the cache hit in the common case.
*
* May still miss the cache if the main loop applies extras this path doesn't
* know about (coordinator mode, memory-mechanics prompt). That's acceptable —
* the alternative is returning null and failing the side question entirely.
*/
export async function buildSideQuestionFallbackParams({
tools,
commands,
mcpClients,
messages,
readFileState,
getAppState,
setAppState,
customSystemPrompt,
appendSystemPrompt,
thinkingConfig,
agents,
}: {
tools: Tools
commands: Command[]
mcpClients: MCPServerConnection[]
messages: Message[]
readFileState: FileStateCache
getAppState: () => AppState
setAppState: (f: (prev: AppState) => AppState) => void
customSystemPrompt: string | undefined
appendSystemPrompt: string | undefined
thinkingConfig: ThinkingConfig | undefined
agents: AgentDefinition[]
}): Promise<CacheSafeParams> {
const mainLoopModel = getMainLoopModel()
const appState = getAppState()
const { defaultSystemPrompt, userContext, systemContext } =
await fetchSystemPromptParts({
tools,
mainLoopModel,
additionalWorkingDirectories: Array.from(
appState.toolPermissionContext.additionalWorkingDirectories.keys(),
),
mcpClients,
customSystemPrompt,
})
const systemPrompt = asSystemPrompt([
...(customSystemPrompt !== undefined
? [customSystemPrompt]
: defaultSystemPrompt),
...(appendSystemPrompt ? [appendSystemPrompt] : []),
])
// Strip in-progress assistant message (stop_reason === null) — same guard
// as btw.tsx. The SDK can fire side_question mid-turn.
const last = messages.at(-1)
const forkContextMessages =
last?.type === 'assistant' && last.message!.stop_reason === null
? messages.slice(0, -1)
: messages
const toolUseContext: ToolUseContext = {
options: {
commands,
debug: false,
mainLoopModel,
tools,
verbose: false,
thinkingConfig:
thinkingConfig ??
(shouldEnableThinkingByDefault() !== false
? { type: 'adaptive' }
: { type: 'disabled' }),
mcpClients,
mcpResources: {},
isNonInteractiveSession: true,
agentDefinitions: { activeAgents: agents, allAgents: [] },
customSystemPrompt,
appendSystemPrompt,
},
abortController: createAbortController(),
readFileState,
getAppState,
setAppState,
messages: forkContextMessages,
setInProgressToolUseIDs: () => {},
setResponseLength: () => {},
updateFileHistoryState: () => {},
updateAttributionState: () => {},
}
return {
systemPrompt,
userContext,
systemContext,
toolUseContext,
forkContextMessages,
}
}