Files
claude-code/src/components/TrustDialog/utils.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

246 lines
6.9 KiB
TypeScript

import type { PermissionRule } from 'src/utils/permissions/PermissionRule.js'
import { getSettingsForSource } from 'src/utils/settings/settings.js'
import type { SettingsJson } from 'src/utils/settings/types.js'
import { BASH_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/BashTool/toolName.js'
import { SAFE_ENV_VARS } from '../../utils/managedEnvConstants.js'
import { getPermissionRulesForSource } from '../../utils/permissions/permissionsLoader.js'
function hasHooks(settings: SettingsJson | null): boolean {
if (settings === null || settings.disableAllHooks) {
return false
}
if (settings.statusLine) {
return true
}
if (settings.fileSuggestion) {
return true
}
if (!settings.hooks) {
return false
}
for (const hookConfig of Object.values(settings.hooks)) {
if (hookConfig.length > 0) {
return true
}
}
return false
}
export function getHooksSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasHooks(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasHooks(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}
function hasBashPermission(rules: PermissionRule[]): boolean {
return rules.some(
rule =>
rule.ruleBehavior === 'allow' &&
(rule.ruleValue.toolName === BASH_TOOL_NAME ||
rule.ruleValue.toolName.startsWith(BASH_TOOL_NAME + '(')),
)
}
/**
* Get which setting sources have bash allow rules.
* Returns an array of file paths that have bash permissions.
*/
export function getBashPermissionSources(): string[] {
const sources: string[] = []
const projectRules = getPermissionRulesForSource('projectSettings')
if (hasBashPermission(projectRules)) {
sources.push('.claude/settings.json')
}
const localRules = getPermissionRulesForSource('localSettings')
if (hasBashPermission(localRules)) {
sources.push('.claude/settings.local.json')
}
return sources
}
/**
* Format a list of items with proper "and" conjunction.
* @param items - Array of items to format
* @param limit - Optional limit for how many items to show before summarizing (ignored if 0)
*/
export function formatListWithAnd(items: string[], limit?: number): string {
if (items.length === 0) return ''
// Ignore limit if it's 0
const effectiveLimit = limit === 0 ? undefined : limit
// If no limit or items are within limit, use normal formatting
if (!effectiveLimit || items.length <= effectiveLimit) {
if (items.length === 1) return items[0]!
if (items.length === 2) return `${items[0]} and ${items[1]}`
const lastItem = items[items.length - 1]!
const allButLast = items.slice(0, -1)
return `${allButLast.join(', ')}, and ${lastItem}`
}
// If we have more items than the limit, show first few and count the rest
const shown = items.slice(0, effectiveLimit)
const remaining = items.length - effectiveLimit
if (shown.length === 1) {
return `${shown[0]} and ${remaining} more`
}
return `${shown.join(', ')}, and ${remaining} more`
}
/**
* Check if settings have otelHeadersHelper configured
*/
function hasOtelHeadersHelper(settings: SettingsJson | null): boolean {
return !!settings?.otelHeadersHelper
}
/**
* Get which setting sources have otelHeadersHelper configured.
* Returns an array of file paths that have otelHeadersHelper.
*/
export function getOtelHeadersHelperSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasOtelHeadersHelper(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasOtelHeadersHelper(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}
/**
* Check if settings have apiKeyHelper configured
*/
function hasApiKeyHelper(settings: SettingsJson | null): boolean {
return !!settings?.apiKeyHelper
}
/**
* Get which setting sources have apiKeyHelper configured.
* Returns an array of file paths that have apiKeyHelper.
*/
export function getApiKeyHelperSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasApiKeyHelper(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasApiKeyHelper(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}
/**
* Check if settings have AWS commands configured
*/
function hasAwsCommands(settings: SettingsJson | null): boolean {
return !!(settings?.awsAuthRefresh || settings?.awsCredentialExport)
}
/**
* Get which setting sources have AWS commands configured.
* Returns an array of file paths that have awsAuthRefresh or awsCredentialExport.
*/
export function getAwsCommandsSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasAwsCommands(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasAwsCommands(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}
/**
* Check if settings have GCP commands configured
*/
function hasGcpCommands(settings: SettingsJson | null): boolean {
return !!settings?.gcpAuthRefresh
}
/**
* Get which setting sources have GCP commands configured.
* Returns an array of file paths that have gcpAuthRefresh.
*/
export function getGcpCommandsSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasGcpCommands(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasGcpCommands(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}
/**
* Check if settings have dangerous environment variables configured.
* Any env var NOT in SAFE_ENV_VARS is considered dangerous.
*/
function hasDangerousEnvVars(settings: SettingsJson | null): boolean {
if (!settings?.env) {
return false
}
return Object.keys(settings.env).some(
key => !SAFE_ENV_VARS.has(key.toUpperCase()),
)
}
/**
* Get which setting sources have dangerous environment variables configured.
* Returns an array of file paths that have env vars not in SAFE_ENV_VARS.
*/
export function getDangerousEnvVarsSources(): string[] {
const sources: string[] = []
const projectSettings = getSettingsForSource('projectSettings')
if (hasDangerousEnvVars(projectSettings)) {
sources.push('.claude/settings.json')
}
const localSettings = getSettingsForSource('localSettings')
if (hasDangerousEnvVars(localSettings)) {
sources.push('.claude/settings.local.json')
}
return sources
}