内置技能使用 TypeScript 实现而非纯 Markdown 文件,是因为它们需要动态逻辑、复杂交互和高级功能,这些是静态 Markdown 无法提供的。
---
name: deploy
description: 部署应用到生产环境
when_to_use: 当用户想要部署应用时使用
---
# 部署技能
## 目标
将应用部署到指定的生产环境。
## 步骤
### 1. 运行测试
执行测试套件确保代码质量。
**成功标准**: 所有测试通过
### 2. 构建应用
构建生产版本的应用程序。
**成功标准**: 构建成功,无错误
### 3. 部署到环境
将构建的应用部署到 `$env` 环境。
**成功标准**: 部署成功,应用可访问特点:
- ✅ 纯静态内容
- ✅ 易于编辑和维护
- ✅ 结构清晰,易于理解
- ❌ 无法包含动态逻辑
- ❌ 无法与代码库深度集成
// src/skills/bundled/simplify.ts
import { AGENT_TOOL_NAME } from '../../tools/AgentTool/constants.js'
import { registerBundledSkill } from '../bundledSkills.js'
const SIMPLIFY_PROMPT = `# Simplify: Code Review and Cleanup
Review all changed files for reuse, quality, and efficiency. Fix any issues found.
## Phase 1: Identify Changes
Run \`git diff\` (or \`git diff HEAD\` if there are staged changes) to see what changed...
## Phase 2: Launch Three Review Agents in Parallel
Use of ${AGENT_TOOL_NAME} tool to launch all three agents concurrently...
`
export function registerSimplifySkill(): void {
registerBundledSkill({
name: 'simplify',
description: 'Review changed code for reuse, quality, and efficiency...',
userInvocable: true,
async getPromptForCommand(args) {
let prompt = SIMPLIFY_PROMPT
if (args) {
prompt += `\n\n## Additional Focus\n\n${args}`
}
return [{ type: 'text', text: prompt }]
},
})
}特点:
- ✅ 包含动态逻辑
- ✅ 可以调用其他工具和API
- ✅ 支持条件渲染
- ✅ 可以访问系统状态
- ✅ 支持复杂的交互流程
- ❌ 需要编译
- ❌ 修改需要重新构建
---
name: deploy
---
# 部署技能
## 目标
部署应用到生产环境。问题:
- 提示内容固定,无法根据上下文调整
- 无法动态生成内容
- 无法处理用户输入参数
async getPromptForCommand(args) {
let prompt = SIMPLIFY_PROMPT
if (args) {
prompt += `\n\n## Additional Focus\n\n${args}`
}
return [{ type: 'text', text: prompt }]
}优势:
- 可以根据参数动态生成提示
- 支持条件逻辑
- 可以访问会话上下文
---
allowed_tools:
- Read
- Write
---限制:
- 只能声明权限,不能主动调用工具
- 无法在提示生成时执行逻辑
import { AGENT_TOOL_NAME } from '../../tools/AgentTool/constants.js'
const SIMPLIFY_PROMPT = `# Simplify
Use ${AGENT_TOOL_NAME} tool to launch all three agents concurrently...
`
export function registerSimplifySkill(): void {
registerBundledSkill({
name: 'simplify',
// ... 其他配置
})
}优势:
- 可以直接调用其他工具
- 可以引用工具常量和配置
- 支持复杂的多步骤流程
## 步骤
### 1. 运行测试
执行测试套件...
**成功标准**: 所有测试通过限制:
- 步骤是线性的,无法有分支逻辑
- 无法根据条件跳过步骤
- 无法处理异常情况
// src/skills/bundled/skillify.ts
async getPromptForCommand(args, context) {
const sessionMemory = await getSessionMemoryContent()
const userMessages = extractUserMessages(
getMessagesAfterCompactBoundary(context.messages),
)
const prompt = SKILLIFY_PROMPT
.replace('{{sessionMemory}}', sessionMemory)
.replace('{{userMessages}}', userMessages.join('\n\n'))
return [{ type: 'text', text: prompt }]
}优势:
- 可以访问会话状态和消息历史
- 支持复杂的模板替换
- 可以执行异步操作
- 支持错误处理和重试逻辑
---
name: deploy
---
# 部署技能
## 步骤
...限制:
- 无法创建临时文件
- 无法读取系统文件
- 无法执行文件系统操作
// src/skills/bundled/batch.ts
import { mkdir, open } from 'fs/promises'
import { join } from 'path'
async function extractBundledSkillFiles(
skillName: string,
files: Record<string, string>,
): Promise<string | null> {
const dir = getBundledSkillExtractDir(skillName)
try {
await writeSkillFiles(dir, files)
return dir
} catch (e) {
logForDebugging(`Failed to extract bundled skill '${skillName}'...`)
return null
}
}优势:
- 可以创建临时文件和目录
- 可以提取内置的参考文件
- 支持文件权限管理
- 可以清理临时资源
---
name: deploy
---
# 部署技能
## 步骤
...限制:
- 无法检查运行环境
- 无法根据条件启用/禁用功能
- 无法访问系统配置
// src/skills/bundled/verify.ts
export function registerVerifySkill(): void {
if (process.env.USER_TYPE !== 'ant') {
return // 只在ANT环境中注册
}
registerBundledSkill({
name: 'verify',
// ... 配置
})
}优势:
- 可以检查环境变量
- 支持功能开关
- 可以根据条件启用/禁用技能
- 支持多环境配置
文件位置: src/skills/bundled/simplify.ts
TypeScript 实现的必要性:
- 多代理协调
import { AGENT_TOOL_NAME } from '../../tools/AgentTool/constants.js'
const SIMPLIFY_PROMPT = `# Simplify: Code Review and Cleanup
## Phase 2: Launch Three Review Agents in Parallel
Use ${AGENT_TOOL_NAME} tool to launch all three agents concurrently...
`为什么需要 TypeScript:
- 需要引用工具常量
AGENT_TOOL_NAME - 需要动态生成包含工具调用的提示
- 需要协调多个代理的并行执行
- 动态参数处理
async getPromptForCommand(args) {
let prompt = SIMPLIFY_PROMPT
if (args) {
prompt += `\n\n## Additional Focus\n\n${args}`
}
return [{ type: 'text', text: prompt }]
}为什么需要 TypeScript:
- 需要根据用户输入动态调整提示
- 需要条件性地添加额外内容
文件位置: src/skills/bundled/skillify.ts
TypeScript 实现的复杂性:
- 会话状态访问
import { getSessionMemoryContent } from '../../services/SessionMemory/sessionMemoryUtils.js'
import { getMessagesAfterCompactBoundary } from '../../utils/messages.js'
function extractUserMessages(messages: Message[]): string[] {
return messages
.filter((m): m is Extract<typeof m, { type: 'user' }> => m.type === 'user')
.map(m => {
const content = m.message.content
if (typeof content === 'string') return content
return content
.filter((b): b is Extract<typeof b, { type: 'text' }> => b.type === 'text')
.map(b => b.text)
.join('\n')
})
.filter(text => text.trim().length > 0)
}为什么需要 TypeScript:
- 需要访问会话内存
- 需要过滤和处理消息历史
- 需要复杂的消息类型转换
- 模板替换
const SKILLIFY_PROMPT = `# Skillify {{userDescriptionBlock}}
Here is session memory summary:
<session_memory>
{{sessionMemory}}
</session_memory>
Here are user's messages:
<user_messages>
{{userMessages}}
</user_messages>为什么需要 TypeScript:
- 需要动态替换模板变量
- 需要处理复杂的嵌套结构
- 需要格式化和清理数据
- 异步操作
async getPromptForCommand(args, context) {
const sessionMemory = await getSessionMemoryContent()
const userMessages = extractUserMessages(
getMessagesAfterCompactBoundary(context.messages),
)
const prompt = SKILLIFY_PROMPT
.replace('{{sessionMemory}}', sessionMemory)
.replace('{{userMessages}}', userMessages.join('\n\n'))
return [{ type: 'text', text: prompt }]
}为什么需要 TypeScript:
- 需要等待异步操作完成
- 需要处理Promise和错误
- 需要访问上下文对象
文件位置: src/skills/bundled/batch.ts
TypeScript 实现的高级功能:
- 环境检查
import { getIsGit } from '../../utils/git.js'
export function registerBatchSkill(): void {
registerBundledSkill({
name: 'batch',
async getPromptForCommand(args) {
const isGit = await getIsGit()
if (!isGit) {
return [{ type: 'text', text: NOT_A_GIT_REPO_MESSAGE }]
}
// ...
},
})
}为什么需要 TypeScript:
- 需要检查是否在Git仓库中
- 需要根据环境条件返回不同消息
- 需要提供有用的错误信息
- 字符串模板构建
function buildPrompt(instruction: string): string {
return `# Batch: Parallel Work Orchestration
You are orchestrating a large, parallelizable change across this codebase.
## User Instruction
${instruction}
## Phase 1: Research and Plan (Plan Mode)
Call of \`${ENTER_PLAN_MODE_TOOL_NAME}\` tool now to enter plan mode, then:
...
`
}为什么需要 TypeScript:
- 需要动态构建复杂的提示模板
- 需要嵌入多个工具常量
- 需要保持代码的可维护性
文件位置: src/skills/bundled/debug.ts
TypeScript 实现的系统集成:
- 文件系统操作
import { open, stat } from 'fs/promises'
import { enableDebugLogging, getDebugLogPath } from '../../utils/debug.js'
export function registerDebugSkill(): void {
registerBundledSkill({
name: 'debug',
async getPromptForCommand(args) {
const wasAlreadyLogging = enableDebugLogging()
const debugLogPath = getDebugLogPath()
let logInfo: string
try {
const stats = await stat(debugLogPath)
const readSize = Math.min(stats.size, TAIL_READ_BYTES)
const fd = await open(debugLogPath, 'r')
try {
const { buffer, bytesRead } = await fd.read({
buffer: Buffer.alloc(readSize),
position: startOffset,
})
const tail = buffer.toString('utf-8', 0, bytesRead)
// ... 处理日志内容
} finally {
await fd.close()
}
} catch (e) {
logInfo = isENOENT(e)
? 'No debug log exists yet...'
: `Failed to read debug log: ${errorMessage(e)}`
}
// ...
},
})
}为什么需要 TypeScript:
- 需要读取文件系统
- 需要处理文件权限和错误
- 需要管理文件描述符
- 需要解析和格式化日志内容
- 系统状态管理
const wasAlreadyLogging = enableDebugLogging()为什么需要 TypeScript:
- 需要调用系统API启用功能
- 需要跟踪状态变化
- 需要提供条件性反馈
import type { ToolUseContext } from '../Tool.js'
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
async getPromptForCommand(
args: string,
context: ToolUseContext,
): Promise<ContentBlockParam[]>优势:
- 编译时类型检查
- IDE智能提示
- 重构安全性
- 接口一致性
import { AGENT_TOOL_NAME } from '../../tools/AgentTool/constants.js'
import { getSessionMemoryContent } from '../../services/SessionMemory/sessionMemoryUtils.js'优势:
- 可以导入和复用现有模块
- 避免代码重复
- 统一的API接口
// 可以编写单元测试
describe('simplify skill', () => {
it('should generate prompt with additional focus', () => {
const result = await getPromptForCommand('focus on performance')
expect(result[0].text).toContain('Additional Focus')
})
})优势:
- 可以进行自动化测试
- 可以模拟各种场景
- 可以验证边界情况
// 缓存和记忆化
const SIMPLIFY_PROMPT = `# Simplify...` // 编译时常量
// 懒加载
if (process.env.USER_TYPE !== 'ant') {
return
}优势:
- 编译时优化
- 死代码消除
- 条件编译
// 清晰的模块边界
export function registerSimplifySkill(): void {
registerBundledSkill({
name: 'simplify',
// ... 配置
})
}优势:
- 模块化设计
- 清晰的依赖关系
- 易于调试和更新
- ✅ 用户友好:易于编辑和理解
- ✅ 快速迭代:无需重新编译
- ✅ 透明性:内容一目了然
- ✅ 灵活性:支持任意复杂度的工作流
- ✅ 功能强大:支持复杂逻辑和系统集成
- ✅ 性能优化:编译时优化和缓存
- ✅ 类型安全:减少运行时错误
- ✅ 可测试:支持自动化测试
- ✅ 版本控制:代码级别的版本管理
- 简单的工作流
- 主要包含文本内容
- 需要频繁修改
- 用户自定义的工作流
- 复杂的多步骤流程
- 需要与系统深度集成
- 需要性能优化
- 需要类型安全和测试
- 官方提供的高质量技能
// src/skills/bundledSkills.ts
export function registerBundledSkill(definition: BundledSkillDefinition): void {
const { files } = definition
// 处理内置文件
if (files && Object.keys(files).length > 0) {
skillRoot = getBundledSkillExtractDir(definition.name)
// 懒提取文件到磁盘
getPromptForCommand = async (args, ctx) => {
extractionPromise ??= extractBundledSkillFiles(definition.name, files)
const extractedDir = await extractionPromise
const blocks = await inner(args, ctx)
if (extractedDir === null) return blocks
return prependBaseDir(blocks, extractedDir)
}
}
// 创建命令对象
const command: Command = {
type: 'prompt',
name: definition.name,
description: definition.description,
// ... 其他属性
getPromptForCommand,
}
bundledSkills.push(command)
}async function extractBundledSkillFiles(
skillName: string,
files: Record<string, string>,
): Promise<string | null> {
const dir = getBundledSkillExtractDir(skillName)
try {
await writeSkillFiles(dir, files)
return dir
} catch (e) {
logForDebugging(`Failed to extract bundled skill '${skillName}'...`)
return null
}
}
async function writeSkillFiles(
dir: string,
files: Record<string, string>,
): Promise<void> {
// 按父目录分组以优化创建
const byParent = new Map<string, [string, string][]>()
for (const [relPath, content] of Object.entries(files)) {
const target = resolveSkillFilePath(dir, relPath)
const parent = dirname(target)
const entry: [string, string] = [target, content]
const group = byParent.get(parent)
if (group) group.push(entry)
else byParent.set(parent, [entry])
}
// 并行创建目录和文件
await Promise.all(
[...byParent].map(async ([parent, entries]) => {
await mkdir(parent, { recursive: true, mode: 0o700 })
await Promise.all(entries.map(([p, c]) => safeWriteFile(p, c)))
}),
)
}内置技能使用 TypeScript 实现是因为它们需要:
- 动态逻辑:根据上下文和输入生成不同的提示
- 系统集成:调用其他工具、访问系统状态、操作文件系统
- 复杂交互:多步骤流程、条件分支、异步操作
- 类型安全:编译时检查、IDE支持、重构安全
- 性能优化:编译时优化、缓存、死代码消除
- 可测试性:单元测试、集成测试、场景模拟
- 维护性:模块化、清晰的依赖、版本控制
Claude Code 采用混合架构:
- 用户技能:使用Markdown,提供简单、灵活的工作流定义
- 内置技能:使用TypeScript,提供强大、可靠、高性能的核心功能
这种设计既保证了易用性(用户可以轻松创建技能),又确保了功能强大性(内置技能可以处理复杂场景)。
如果需要创建类似内置技能的复杂技能:
- 评估复杂度:是否需要动态逻辑、系统集成、性能优化
- 选择实现方式:
- 简单场景 → Markdown 用户技能
- 复杂场景 → TypeScript 内置技能
- 考虑贡献:对于通用技能,可以考虑贡献到官方仓库
结论:TypeScript 实现的内置技能是 Claude Code 架构设计的必然选择,它们提供了Markdown技能无法实现的复杂功能、系统集成和性能优化,同时保持了类型安全和可维护性。