附录 IV:自动化与调度

# 附录 IV:自动化与调度

> 最好的工具不只响应你的请求,还在你忘记的时候替你记着。

---

## 附录 IV-A:Cron 调度工具

### 功能描述

Agent 可以创建定时任务,让指定 Prompt 在未来某个时刻(或按计划重复)自动执行,无需用户每次手动触发。

### 核心文件

| 文件 | 作用 |
|------|------|
| `src/tools/ScheduleCronTool/CronCreateTool.ts` | `CronCreateTool`:LLM 创建定时任务 |
| `src/tools/ScheduleCronTool/CronDeleteTool.ts` | `CronDeleteTool`:LLM 删除定时任务 |
| `src/tools/ScheduleCronTool/CronListTool.ts` | `CronListTool`:LLM 列出所有任务 |
| `src/tools/ScheduleCronTool/prompt.ts` | 工具描述与常量(`DEFAULT_MAX_AGE_DAYS`、`isDurableCronEnabled` 等) |
| `src/utils/cronTasks.ts` | 任务持久化(`.claude/scheduled_tasks.json`)与调度执行 |
| `src/utils/cron.ts` | Cron 表达式解析(`parseCronExpression`、`cronToHuman`、`nextCronRunMs`) |

### 输入参数

```typescript
// CronCreateTool 输入(Zod schema)
{
  cron: string,       // 标准 5 字段 cron 表达式(本地时间)
                      // "*/5 * * * *" = 每 5 分钟
                      // "30 14 28 2 *" = 2 月 28 日 14:30

  prompt: string,     // 触发时执行的 Prompt

  recurring: boolean, // true(默认)= 重复触发,DEFAULT_MAX_AGE_DAYS 天后自动过期
                      // false = 只触发一次后自动删除(适合"提醒我..."场景)

  durable: boolean,   // true  = 持久化到磁盘,Claude Code 重启后恢复
                      // false(默认)= 仅内存,Session 结束后消失
}
```

### 持久化策略

```
durable: false(默认):
  仅存储在内存(setScheduledTasksEnabled 管理)
  Claude Code 进程退出 → 任务消失

durable: true:
  写入 .claude/scheduled_tasks.json
  Claude Code 重启后自动恢复,继续调度
  最多 MAX_JOBS = 50 个活跃任务
```

### 调度精度与限制

- Cron 表达式精度:**分钟级**(标准 5 字段:分 时 日 月 周)
- `cronToHuman()` 将表达式转换为人类可读描述(如 "every 5 minutes")
- `nextCronRunMs()` 计算距下次触发的毫秒数,用于定时器设置

---

## 附录 IV-B:Todo 跟踪工具

### 功能描述

Agent 维护一个结构化的任务清单,在 UI 中实时展示当前任务进度,让用户和 Agent 始终清楚"正在做什么"与"还剩什么"。

### 核心文件

| 文件 | 作用 |
|------|------|
| `src/tools/TodoWriteTool/TodoWriteTool.ts` | `TodoWriteTool`:LLM 写入/更新 todo 列表 |
| `src/tools/TodoWriteTool/constants.ts` | `TODO_WRITE_TOOL_NAME` 等常量 |
| `src/utils/todo/types.ts` | `TodoListSchema`、Todo 状态类型定义 |

### Todo 数据结构

```typescript
// src/utils/todo/types.ts(TodoListSchema)
type Todo = {
  id: number
  title: string                    // 3-7 字简洁动作标签
  status: 'not-started'            // 未开始
        | 'in-progress'            // 进行中(同时最多 1 个)
        | 'completed'              // 已完成
}
```

### TodoWriteTool 使用约束

Agent 在收到 `TodoWriteTool` 的 Prompt 约束时需遵守:

- 开始某个 todo 前先标为 `in-progress`
- 完成后**立即**标为 `completed`(不批量完成)
- 同一时刻只有一个 todo 处于 `in-progress`

这些约束使 UI 能精确展示 Agent 的实时状态,而不是一次性刷新全部状态。

### V2 版本门控

```typescript
// TodoWriteTool.ts
isEnabled() {
  return !isTodoV2Enabled()   // V2 启用时,本工具让位给新实现
}
```

`isTodoV2Enabled()`(`src/utils/tasks.ts`)通过 GrowthBook 特性标志控制,V2 版本有独立的任务管理实现。

---

## 附录 IV-C:Voice 模式

### 功能描述

通过语音输入与 Claude Code 交互——用户说话,流式转为文字后发送给 Agent。

**核心文件**:`src/voice/`(目录),`src/services/voiceStreamSTT.ts`、`src/services/voiceKeyterms.ts`

### 启用条件(双重门控)

```typescript
// src/voice/voiceModeEnabled.ts

// 1. GrowthBook 功能门控(可远程关闭)
isVoiceGrowthBookEnabled(): boolean
  → feature('VOICE_MODE') && !getFeatureValue('tengu_amber_quartz_disabled')
  // "amber_quartz" 是紧急 kill-switch:可在不发版的情况下远程关闭语音

// 2. 认证检查(仅 OAuth 用户可用)
hasVoiceAuth(): boolean
  → 需要有效的 Anthropic OAuth access_token
  // 语音使用 claude.ai 的 voice_stream 端点,不支持 API Key / Bedrock / Vertex
```

**两个条件都满足**时,`/voice` 命令才可用。

### 架构

```
用户说话
    │
    ├─ 关键词检测(voiceKeyterms.ts)
    │   识别唤醒词或命令关键词,降低 STT 延迟
    │
    ├─ 流式 STT(voiceStreamSTT.ts)
    │   实时将语音流发送给 claude.ai voice_stream 端点
    │   → 逐步返回转写文字(流式)
    │
    └─ 文字写入输入框 → 触发正常的 Agent 对话流程
```

### 使用要求

- 需要 Claude.ai 订阅(Pro/Max),通过 `/login` 登录
- 仅在交互模式下可用(`-p` 非交互模式不支持)
- `CLAUDE_CODE_DISABLE_VOICE=1` 可在环境级别禁用

---

## 附录 IV-D:Vim 键位模式

### 功能描述

在 Claude Code 的命令行输入框中启用 Vim 操作模式,支持完整的 Normal/Insert/Visual 模式切换和常用 Vim 命令。

**核心文件**:`src/vim/`(5 个文件)

### 状态机架构

```
VimState
├── INSERT 模式
│   └── 记录 insertedText(用于 dot-repeat)
│
└── NORMAL 模式(CommandState 子状态机)
    ├── idle         ← 等待命令输入
    ├── count        ← 接收数字前缀(1-9)
    ├── operator     ← 接收操作符(d/c/y)
    ├── operatorCount ← 操作符后的数字前缀
    ├── operatorTextObj ← 等待文本对象(iw/i"/等)
    ├── operatorFind ← 等待查找字符(f/F/t/T)
    ├── find         ← 单字符查找
    ├── g            ← g 前缀命令(gg/gj/gk 等)
    ├── replace      ← r 命令等待替换字符
    └── indent       ← </> 缩进操作
```

### 支持的命令

**光标移动**(`motions.ts`):

| 命令 | 说明 |
|------|------|
| `h j k l` | 左/下/上/右 |
| `w b e` | 词前进/后退/词尾 |
| `0 $` | 行首/行尾 |
| `^` | 第一个非空白字符 |
| `gg G` | 文档首/尾 |
| `f{c} F{c}` | 向前/后查找字符 |
| `t{c} T{c}` | 向前/后查找字符前一位 |

**操作符**(`operators.ts`):

| 命令 | 说明 |
|------|------|
| `d{motion}` | 删除 |
| `c{motion}` | 修改(删除并进入 Insert) |
| `y{motion}` | 复制 |
| `dd cc yy` | 整行操作 |
| `x` | 删除光标字符 |
| `r{c}` | 替换单字符 |
| `p P` | 粘贴(后/前) |

**文本对象**(`textObjects.ts`):

| 命令 | 说明 |
|------|------|
| `iw aw` | 单词内/含空格 |
| `i" a"` | 双引号内/含引号 |
| `i' a'` | 单引号内/含引号 |
| `i( a(` | 括号内/含括号 |

**特殊功能**(`transitions.ts`):

| 命令 | 说明 |
|------|------|
| `.` | Dot-repeat(重复上一个修改操作) |
| `u` | Undo |
| `{N}{cmd}` | 数字前缀(如 `3w`、`5dd`) |

### 最大计数限制

```typescript
// types.ts
const MAX_VIM_COUNT = 999  // 防止意外输入巨大数字导致性能问题
```

---

## 附录 IV 源码快速导航

```
自动化与调度
├── Cron 调度
│   ├── src/tools/ScheduleCronTool/
│   │   ├── CronCreateTool.ts  ← 创建定时任务(含持久化选项)
│   │   ├── CronDeleteTool.ts  ← 删除任务
│   │   ├── CronListTool.ts    ← 列出任务
│   │   └── prompt.ts          ← 工具描述 + isDurableCronEnabled
│   └── src/utils/
│       ├── cronTasks.ts       ← 持久化 + 调度执行
│       └── cron.ts            ← 表达式解析 + cronToHuman
│
├── Todo 跟踪
│   ├── src/tools/TodoWriteTool/
│   │   ├── TodoWriteTool.ts   ← 写入/更新 todo 列表
│   │   └── constants.ts       ← TODO_WRITE_TOOL_NAME
│   └── src/utils/todo/types.ts ← TodoListSchema(状态枚举)
│
├── Voice 模式
│   ├── src/voice/             ← 入口、UI、命令注册
│   │   └── voiceModeEnabled.ts ← 双重门控(GrowthBook + OAuth 检查)
│   ├── src/services/voiceStreamSTT.ts  ← 流式 STT
│   └── src/services/voiceKeyterms.ts  ← 关键词检测
│
└── Vim 键位
    └── src/vim/
        ├── types.ts        ← VimState 状态机类型(含完整状态图注释)
        ├── transitions.ts  ← 主状态转换函数(可扫描查看所有状态)
        ├── motions.ts      ← 光标移动实现
        ├── operators.ts    ← 操作符实现(d/c/y/r 等)
        └── textObjects.ts  ← 文本对象(iw/i"/i( 等)
```

---

→ [附录 V:工作区扩展与运行模式](appendix-V-workspace)