主题色
250
壁纸模式
壁纸设置
特效设置
固定导航栏
字体选择
文章列表布局
3251 字
9 分钟
Claude Code 内置子命令深度解析:/compact、/simplify 与 /goal
2026-07-25

Claude Code 的三个斜杠命令采用了不同的运行时机制。/compact 改写会话上下文,/simplify 通过 bundled Skill 注入代码审查流程,/goal 用状态、工具调用和自动续跑管理长任务。

核心 CLI 没有完整开源。Anthropic 官方仓库提供 CHANGELOG、插件和扩展类型;具体调用链引用 Claude Code Best 的 TypeScript 复刻实现,并参考 Open-ClaudeCode 的 npm source map 归档。复刻代码只用于说明机制。

2.1.63 CHANGELOG 将 /simplify 和 /batch 列为 bundled slash command;claude-code.d.ts 将 $.session.compact() 定义为与 /compact 相同的会话压缩入口。

一、三个命令的注册类型#

复刻实现用 Command 联合类型区分命令的加载和执行协议:

export type LocalCommandResult =
| { type: 'text'; value: string }
| { type: 'compact'; compactionResult: CompactionResult }
| { type: 'skip' }
type LocalCommand = {
type: 'local'
supportsNonInteractive: boolean
load: () => Promise<LocalCommandModule>
}
type LocalJSXCommand = {
type: 'local-jsx'
load: () => Promise<LocalJSXCommandModule>
}
export type PromptCommand = {
type: 'prompt'
source: SettingSource | 'builtin' | 'plugin' | 'bundled'
getPromptForCommand(
args: string,
context: ToolUseContext,
): Promise<ContentBlockParam[]>
}

这段类型定义位于复刻实现的 src/types/command.ts。

三个命令对应的注册类型如下:

命令注册类型主要副作用复刻实现的源码入口
/compactlocal用摘要和边界消息替换旧上下文src/commands/compact/index.ts
/simplifyprompt,来源为 bundled给当前模型注入一套审查和修复流程src/skills/bundled/simplify.ts
/goallocal-jsx创建或改变持久目标,并触发后续自动查询src/commands/goal/index.ts

它们经过同一个命令发现器,执行路径如下:

flowchart TD A[用户输入 /command] --> B[命令发现与懒加载] B --> C{Command.type} C -->|local| D[/compact.call] C -->|prompt| E[/simplify.getPromptForCommand] C -->|local-jsx| F[/goal.call] D --> G[重写消息历史] E --> H[模型按提示词调用 Agent 工具] F --> I[GoalState + GoalTool + idle hook] I --> J[下一轮自动查询]

在复刻实现的 src/commands.ts 中,compact 直接加入 COMMANDS 数组,goal 只在 GOAL feature flag 打开时加入。bundled Skill 通过 registerBundledSkill() 注册,再由 getSkills() 与文件型 Skill、插件 Skill 合并。因此,/simplify 虽然出现在 slash command 菜单中,运行时仍按 Skill 处理。

二、/compact:一次受控的上下文重写#

2.1 注册阶段只描述能力,不执行压缩#

注册对象只描述命令元数据和懒加载入口:

const compact = {
type: 'local',
name: 'compact',
description:
'Clear conversation history but keep a summary in context. Optional: /compact [instructions for summarization]',
isEnabled: () => !isEnvTruthy(process.env.DISABLE_COMPACT),
supportsNonInteractive: true,
argumentHint: '<optional custom summarization instructions>',
load: () => import('./compact.js'),
} satisfies Command

这段注册代码体现了两点:

  1. load() 使用动态 import,启动时只把命令元数据放进菜单,不提前加载摘要、Hook、token 计算和 API 调用相关模块。
  2. DISABLE_COMPACT 只影响命令是否可用。自动压缩是否开启还要经过另一套配置和 feature flag 判断,因此“隐藏 /compact”与“关闭自动压缩”不是同一个开关。

2.2 手动命令的主调用链#

src/commands/compact/compact.ts 的 call() 可以压缩成下面的伪代码:

/compact args
-> getMessagesAfterCompactBoundary(messages)
-> 没有自定义指令时,先尝试 session-memory compaction
-> reactive-only 模式走 reactiveCompactOnPromptTooLong
-> microcompactMessages(messages)
-> compactConversation(messagesForCompact, customInstructions)
-> 返回 { type: "compact", compactionResult }

call() 先调用 getMessagesAfterCompactBoundary(),只处理最近一个压缩边界之后的消息。消息为空时,命令直接报错。

2.3 Micro Compact 先处理工具结果#

microcompactMessages() 先处理工具结果。在启用 cached microcompact 且模型受支持时,源码扫描可压缩的 tool_result,按消息分组记录它们,再通过 cache edit 删除旧结果。消息本身可以保持不变,删除操作延迟到 API 层执行。

完整压缩前,代码会先尝试 microcompact:

flowchart LR A[当前消息] --> B{可用的 microcompact 路径?} B -->|是| C[删除或标记旧工具结果] B -->|否| D[保留原消息] C --> E[compactConversation] D --> E E --> F[生成摘要]

cached microcompact 只允许主线程使用,子 Agent 不会把自己的工具 ID 写入主线程的全局缓存状态。

2.4 compactConversation() 做了什么#

摘要过程位于 src/services/compact/compact.ts 的 compactConversation()。

记录 token 并运行 PreCompact hooks。

函数先用 tokenCountWithEstimation(messages) 估算 preCompactTokenCount,再触发 executePreCompactHooks()。Hook 可以返回新的摘要指令,最终通过 mergeHookInstructions() 与用户在 /compact ... 后面写的指令合并,用户指令排在前面。

构造摘要请求。

getCompactPrompt() 要求模型只输出文本,不调用工具,并使用 <analysis> 和 <summary> 标签。摘要请求复用主会话的工具 Schema 时,工具调用会使这次压缩请求失去摘要文本。

摘要提示词要求保留用户目标、文件名、函数签名、代码修改、错误修复和安全约束。formatCompactSummary() 随后移除 <analysis> 草稿,只把 <summary> 写回会话。

优先复用 prompt cache。

streamCompactSummary() 首选 runForkedAgent(),复用主会话的系统提示词、工具和缓存前缀。失败或开关关闭时,代码调用 queryModelWithStreaming(),并执行以下处理:

  • 把图片和文档替换成 [image]、[document] 标记,避免摘要请求本身因为媒体超限;
  • 移除压缩后会重新注入的 Skill attachment;
  • 禁用 thinking,只保留文本摘要;
  • 只允许读取类工具,且拒绝工具调用。

处理摘要请求超长。

历史过长时,摘要请求本身也可能超出窗口。代码会重试,并按 API round 丢弃最早的一组消息;仍然失败时返回 Conversation too long to summarize,提示用户使用 /clear。

保存需要恢复的上下文。

摘要生成后,代码保存 readFileState,清理读取缓存,并生成几类附件:最近读取的少量文件、仍在运行的 Agent 信息、计划文件、plan mode 指令、已经调用过的 Skill,以及工具、Agent、MCP 指令的 delta。复刻实现限制了单文件、单 Skill 和总附件的 token 数,也限制了恢复文件数量。

原始 transcript 仍然存在。当前 API 看到的是摘要、保留的近期消息和这些附件。

写入边界、摘要消息和 Hook 结果。

createCompactBoundaryMessage() 生成一个 system / compact_boundary 消息,记录触发方式、压缩前 token 数以及最后一条旧消息的 UUID。之后按固定顺序组装新消息:

boundaryMarker
summaryMessages
messagesToKeep
attachments
hookResults

QueryEngine 和 SDK 层识别这个边界。官方 SDK 类型说明,$.session.compact() 与 /compact 触发同一事件,压缩后的 transcript 由摘要和保留消息组成,消息流中可以观察到 compact_boundary。

2.5 手动压缩、自动压缩和 Reactive Compact#

手动、自动和 Reactive Compact 共用 compactConversation(),触发路径不同:

触发方式入口特点
手动 /compactcommands/compact/compact.ts支持用户附加摘要指令,成功后立即返回本地命令结果
自动压缩services/compact/autoCompact.ts由 token 阈值和配置触发,带递归保护和失败熔断
Reactive CompactreactiveCompactOnPromptTooLong()等 API 返回 prompt-too-long 后再压缩,由实验开关控制

官方类型定义把 session.compact 的 trigger 暴露为 plugin,并允许 Hook veto。cached microcompact、reactive 模式和具体阈值属于版本实现细节。

三、/simplify:并行审查流程#

3.1 官方确认它是 bundled slash command#

Anthropic 官方 CHANGELOG 的 2.1.63 条目写明“Added /simplify and /batch bundled slash commands”。核心 CLI 的完整实现没有在官方仓库公开。

在 Claude Code Best 中,/simplify 的实现位于 src/skills/bundled/simplify.ts:

export function registerSimplifySkill(): void {
registerBundledSkill({
name: 'simplify',
description:
'Review changed code for reuse, quality, and efficiency, then fix any issues found.',
userInvocable: true,
async getPromptForCommand(args) {
let prompt = SIMPLIFY_PROMPT
if (args) prompt += `\n\n## Additional Focus\n\n${args}`
return [{ type: 'text', text: prompt }]
},
})
}

registerBundledSkill() 将它转换成 type: 'prompt' 命令,并标记 source: 'bundled'、loadedFrom: 'bundled'。命令发现器再把它与项目 .claude/skills 和插件 Skill 合并。

3.2 Prompt 的三个阶段#

SIMPLIFY_PROMPT 为主 Agent 规定了三个阶段。

确定变更集合。

模型先运行 git diff,有暂存区时使用 git diff HEAD;工作树没有变化时,改为审查用户提到或本轮编辑过的文件。

并行启动三个 Agent。

提示词要求在一条消息中并发调用三次 Agent 工具,并把完整 diff 传给每个 Agent:

Agent检查重点
Code Reuse Review搜索已有 helper,识别重复函数、手写路径处理和重复类型守卫
Code Quality Review检查冗余状态、参数膨胀、复制粘贴、泄漏抽象、字符串类型和无意义 JSX 嵌套
Efficiency Review检查重复计算、串行执行、热路径膨胀、内存泄漏和过宽读取

三个 Agent tool call 在同一条模型响应中发出,由工具调度层并发执行。/simplify 本身不包含 AST 分析器或独立的 lint 引擎。

汇总并修复。

主 Agent 等待三个结果,合并发现并修改代码。误报可以跳过,但需要在最终摘要中说明。/simplify 后面的参数会追加到 ## Additional Focus,可用于限定性能、API 兼容性或目录范围。

3.3 执行边界#

/simplify 通过提示词编排审查和修复:

sequenceDiagram participant U as 用户 participant S as bundled Skill participant M as 主 Agent participant A as 三个 Agent 调用 participant T as Edit/Write 工具 U->>S: /simplify [additional focus] S->>M: 注入 SIMPLIFY_PROMPT M->>A: 一次消息并行发起三类审查 A-->>M: reuse / quality / efficiency findings M->>T: 选择并修复问题 T-->>M: 工具结果和权限检查 M-->>U: 修复摘要

它使用正常的权限、工作树和工具调用协议,也不保证采纳每条建议。三个 Agent 的结果取决于模型对 diff、仓库搜索和误报的判断。

四、/goal:跨轮次状态机#

4.1 命令入口#

复刻实现的注册对象如下:

const goal = {
type: 'local-jsx',
name: 'goal',
description: 'Set or view a persistent goal that drives auto-continuation across turns',
argumentHint: '[<objective> | status | clear | pause | resume | complete]',
bridgeSafe: false,
load: () => import('./goal.js'),
} satisfies Command

goal.tsx 处理用户输入和状态操作,支持:

输入状态操作
/goal <objective>创建目标;已有未完成目标时先弹出替换确认框
/goal 或 /goal status显示目标、状态、耗时、token 和续跑轮数
/goal pause / resume暂停或恢复自动续跑
/goal continue达到最大轮数后,清零续跑计数并由用户明确继续
/goal complete手动完成目标
/goal clear清理目标并写入 tombstone,避免恢复会话时复活旧目标

命令完成回调可以设置 shouldQuery: true,也可以插入隐藏的 metaMessages。设置新目标时,源码会插入 <goal-objective-updated> 元消息,UI 则显示截断后的摘要。

4.2 GoalState#

状态机位于复刻实现的 src/services/goal/goalState.ts,并按 session ID 存在 Map<string, GoalState> 中。关键字段包括:

type GoalState = {
objective: string
status: 'active' | 'paused' | 'blocked' | 'budget_limited'
| 'usage_limited' | 'max_turns' | 'complete'
tokenBudget: number | null
tokensUsed: number
startTime: number
pausedAt: number | null
accumulatedActiveMs: number
blockedAttempts: number
lastBlockReason: string | null
turnsExecuted: number
}

状态转移规则如下:

  • pause 把当前 active 时间累加到 accumulatedActiveMs,恢复时重新设置 startTime,所以展示的是有效工作时间,不是墙上时钟时间。
  • token 计数在 cost tracker 收到每次 API usage 后更新,累计达到 tokenBudget 就转成 budget_limited。
  • 连续三次相同原因的 blocked 报告才会转成 blocked;原因变化会重置连续计数。
  • 自动续跑最多 MAX_GOAL_TURNS = 150 轮,达到上限后变成 max_turns,必须由用户输入 /goal continue 才能清零重启。

goalStorage.ts 每次变更都会向当前会话的 JSONL transcript 写入 goal checkpoint。JSONL 是每行一个 JSON 记录;/goal clear 追加 goal-cleared tombstone(删除标记)。恢复会话时,读取器按 session ID 取最后一个状态,/resume 便可恢复目标。

4.3 模型通过 GoalTool 报告完成或阻塞#

模型通过 GoalTool.ts 报告完成或阻塞。工具输入限制为:

z.strictObject({
action: z.enum(['get', 'update']).optional(),
status: z.enum(['complete', 'blocked']).optional(),
reason: z.string().optional(),
})

get 读取当前快照;update 只能将目标标记为 complete 或 blocked。完成时工具生成 token、有效耗时和续跑轮数报告,再调用 completeGoal();阻塞时调用 recordBlockedAttempt(),相同条件连续三轮后才会进入 blocked。

目标状态由应用代码保存,模型只能通过 schema 约束的工具修改它。

4.4 Idle hook 触发下一轮#

自动续跑由 useGoalContinuation() 驱动。Hook 在 isLoading 从 true 变成 false 时检查是否需要下一轮:

一轮查询结束
-> 检查用户队列、活动 JSX、plan mode 和 abort 状态
-> 读取 GoalState
-> budget_limited: 注入一次收尾提示
-> active 且未到 MAX_GOAL_TURNS: turnsExecuted += 1
-> enqueue(<goal-steering type="continuation">...</goal-steering>)
-> 下一轮正常 query

如果队列里有 /goal pause 或普通用户消息,Hook 会跳过本次自动续跑,等用户消息处理完再判断。

buildContinuationPrompt() 生成带审计规则的 meta message:

  1. 保持原始目标范围,不因为一轮没做完就擅自缩小目标。
  2. 完成前执行 Completion Audit,逐项寻找测试、文件内容或命令结果等证据。
  3. 遇到障碍先继续尝试,只有同一阻塞原因连续三轮才允许标记 blocked。
  4. 真正完成或确认阻塞时,通过 GoalTool 更新状态。

系统上下文还会包含一个短 XML 目标摘要:

<active-goal status="active" elapsed="4m 12s" tokens="18400" budget="50000" turns="3">
为 monorepo 添加可回滚的数据库迁移并补齐测试
</active-goal>

这个摘要把目标状态带入每轮上下文,完整的审计规则只在续跑提示中出现。

4.5 token 预算#

src/cost-tracker.ts 根据每次 API 响应的 input、output、cache read 和 cache creation token 调用 updateGoalTokens()。达到预算后,idle hook 注入一次 budget_limit 提示,要求模型停止新的工具调用并汇报已完成和剩余工作。

这条链路可以画成:

flowchart TD A[API response usage] --> B[cost-tracker 累加 token] B --> C{tokensUsed >= tokenBudget?} C -->|否| D[GoalState 保持 active] C -->|是| E[status = budget_limited] E --> F[idle hook 注入收尾 prompt] F --> G[不再启动实质性新工作]

预算控制由应用层执行。用户仍然可以修改或清除目标,模型也可能在收到预算提示前发起最后一个工具调用。

五、把三个命令放在一起比较#

维度/compact/simplify/goal
改变的对象当前会话消息表示当前 Agent 的审查指令跨轮次目标状态和消息队列
核心机制摘要 API + compact boundary + 恢复附件bundled prompt + 三个并行 AgentGoalState + GoalTool + idle hook
是否直接修改历史是,旧消息被摘要替代否,正常工具调用产生修改不直接改历史,但会追加 meta message
是否持久化transcript 记录 compact boundary通过普通文件修改持久化JSONL 中每次写入 goal checkpoint
失败/停止策略prompt-too-long 重试,最终建议 /clear主 Agent 判断误报并跳过budget、blocked、max_turns、pause
实现边界摘要是有损的结果由模型和工具调用决定状态机有硬上限

三者的控制对象不同:

  • /compact 改变“模型还能看到什么”。
  • /simplify 改变“模型如何审查当前改动”。
  • /goal 改变“模型何时继续下一轮,以及何时必须停下”。

六、版本边界#

引用版本:Claude Code Best commit 为 77a7934e15d69da13879112ed7db695c9ee7a52a,Open-ClaudeCode commit 为 2980105e5ae5910d50c2e8adfa0ce6b5e4907bcc。核心 CLI 的实现会随版本变化,实验开关、阈值和内置提示词都可能调整。稳定的部分是命令语义和公开扩展接口;函数名、token 常量和 prompt 文本都不应视为长期契约。

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

赞助
Claude Code 内置子命令深度解析:/compact、/simplify 与 /goal
https://blog.souloss.cn/posts/ai/agents-decoded/claude-code/claude-code-built-in-commands-compact-simplify-goal/
作者
Tsukimi
发布于
2026-07-25
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时