claude-code-best/src/commands/brief.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

131 lines
5.1 KiB
TypeScript

import { feature } from 'bun:bundle'
import { z } from 'zod/v4'
import { getKairosActive, setUserMsgOptIn } from '../bootstrap/state.js'
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js'
import {
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
logEvent,
} from '../services/analytics/index.js'
import type { ToolUseContext } from '../Tool.js'
import { isBriefEntitled } from '@claude-code-best/builtin-tools/tools/BriefTool/BriefTool.js'
import { BRIEF_TOOL_NAME } from '@claude-code-best/builtin-tools/tools/BriefTool/prompt.js'
import type {
Command,
LocalJSXCommandContext,
LocalJSXCommandOnDone,
} from '../types/command.js'
import { lazySchema } from '../utils/lazySchema.js'
// Zod guards against fat-fingered GB pushes (same pattern as pollConfig.ts /
// cronScheduler.ts). A malformed config falls back to DEFAULT_BRIEF_CONFIG
// entirely rather than being partially trusted.
const briefConfigSchema = lazySchema(() =>
z.object({
enable_slash_command: z.boolean(),
}),
)
type BriefConfig = z.infer<ReturnType<typeof briefConfigSchema>>
const DEFAULT_BRIEF_CONFIG: BriefConfig = {
enable_slash_command: false,
}
// No TTL — this gate controls slash-command *visibility*, not a kill switch.
// CACHED_MAY_BE_STALE still has one background-update flip (first call kicks
// off fetch; second call sees fresh value), but no additional flips after that.
// The tool-availability gate (tengu_kairos_brief in isBriefEnabled) keeps its
// 5-min TTL because that one IS a kill switch.
function getBriefConfig(): BriefConfig {
const raw = getFeatureValue_CACHED_MAY_BE_STALE<unknown>(
'tengu_kairos_brief_config',
DEFAULT_BRIEF_CONFIG,
)
const parsed = briefConfigSchema().safeParse(raw)
return parsed.success ? parsed.data : DEFAULT_BRIEF_CONFIG
}
const brief = {
type: 'local-jsx',
name: 'brief',
description: 'Toggle brief-only mode',
isEnabled: () => {
if (feature('KAIROS') || feature('KAIROS_BRIEF')) {
return getBriefConfig().enable_slash_command
}
return false
},
immediate: true,
load: () =>
Promise.resolve({
async call(
onDone: LocalJSXCommandOnDone,
context: ToolUseContext & LocalJSXCommandContext,
): Promise<React.ReactNode> {
const current = context.getAppState().isBriefOnly
const newState = !current
// Entitlement check only gates the on-transition — off is always
// allowed so a user whose GB gate flipped mid-session isn't stuck.
if (newState && !isBriefEntitled()) {
logEvent('tengu_brief_mode_toggled', {
enabled: false,
gated: true,
source:
'slash_command' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
})
onDone('Brief tool is not enabled for your account', {
display: 'system',
})
return null
}
// Two-way: userMsgOptIn tracks isBriefOnly so the tool is available
// exactly when brief mode is on. This invalidates prompt cache on
// each toggle (tool list changes), but a stale tool list is worse —
// when /brief is enabled mid-session the model was previously left
// without the tool, emitting plain text the filter hides.
setUserMsgOptIn(newState)
context.setAppState(prev => {
if (prev.isBriefOnly === newState) return prev
return { ...prev, isBriefOnly: newState }
})
logEvent('tengu_brief_mode_toggled', {
enabled: newState,
gated: false,
source:
'slash_command' as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
})
// The tool list change alone isn't a strong enough signal mid-session
// (model may keep emitting plain text from inertia, or keep calling a
// tool that just vanished). Inject an explicit reminder into the next
// turn's context so the transition is unambiguous.
// Skip when Kairos is active: isBriefEnabled() short-circuits on
// getKairosActive() so the tool never actually leaves the list, and
// the Kairos system prompt already mandates SendUserMessage.
// Inline <system-reminder> wrap — importing wrapInSystemReminder from
// utils/messages.ts pulls constants/xml.ts into the bridge SDK bundle
// via this module's import chain, tripping the excluded-strings check.
const metaMessages = getKairosActive()
? undefined
: [
`<system-reminder>\n${
newState
? `Brief mode is now enabled. Use the ${BRIEF_TOOL_NAME} tool for all user-facing output — plain text outside it is hidden from the user's view.`
: `Brief mode is now disabled. The ${BRIEF_TOOL_NAME} tool is no longer available — reply with plain text.`
}\n</system-reminder>`,
]
onDone(
newState ? 'Brief-only mode enabled' : 'Brief-only mode disabled',
{ display: 'system', metaMessages },
)
return null
},
}),
} satisfies Command
export default brief