小克的魔法书:/resume 命令的奇幻之旅

0 阅读6分钟

想象一下,你是一位名叫“小克”的 AI 编程助手,每天要跟无数开发者聊天。每个对话都是一本魔法日记——里面写满了代码、思考、报错和解决方案。但问题来了:如果用户说“刚才那个 bug 我们再看看”,你该怎么办?难道要失忆吗?

当然不!/resume 就是你的时光回溯魔法。它让你像翻书一样,找到之前的某本日记,打开那一页,然后接着写下去


📖 实现原理:三件魔法神器

1. 会话存档器(Session Archiver)

每次对话,小克都会悄悄把所有消息上下文文件快照(如果有)保存成一个 JSON 文件。就像一个自动写日记的魔法羽毛笔。

// 简化的会话保存逻辑
interface Message {
  role: 'user' | 'assistant' | 'system';
  content: string;
  timestamp: number;
  // 附带的技术元数据
  toolCalls?: ToolCall[];
  fileSnapshots?: Record<string, string>;
}

interface Session {
  id: string;              // 例如 "sess_20240315_143022"
  title: string;           // 自动生成或用户自定义
  messages: Message[];
  cwd: string;             // 工作目录
  createdAt: number;
  lastActiveAt: number;
  // 支持分支!就像 git 的 commit
  parentSessionId?: string; // 从哪个会话恢复来的
  branchName?: string;
}

class SessionArchiver {
  private storageDir = '~/.claude/sessions';
  
  async save(session: Session): Promise<void> {
    const filePath = `${this.storageDir}/${session.id}.json`;
    await fs.writeJson(filePath, session);
  }
  
  async list(): Promise<SessionMeta[]> {
    const files = await fs.readdir(this.storageDir);
    return files
      .filter(f => f.endsWith('.json'))
      .map(f => fs.readJson(`${this.storageDir}/${f}`))
      .sort((a,b) => b.lastActiveAt - a.lastActiveAt);
  }
  
  async load(id: string): Promise<Session> {
    return fs.readJson(`${this.storageDir}/${id}.json`);
  }
}

2. 记忆编织机(Context Weaver)

当你执行 /resume 并选择一个旧会话,小克不会简单地把历史消息“倒带”重放。它会重建整个心理状态

  • 加载所有历史消息(包括 assistant 之前的回复)。
  • 重新注入当时的环境变量、工作目录。
  • 如果涉及文件编辑,恢复文件快照(或依赖 git diff 重建心智模型)。
class ContextWeaver {
  async weave(session: Session): Promise<ClaudeContext> {
    // 1. 构建消息数组(完整历史)
    const messages = session.messages.map(m => ({
      role: m.role,
      content: m.content
    }));
    
    // 2. 恢复文件状态(可选)
    if (session.fileSnapshots) {
      await this.restoreFileSnapshots(session.fileSnapshots);
    }
    
    // 3. 注入系统提示:“你正在恢复 2024-03-15 的会话,以下是之前的对话...”
    const systemPrompt = this.buildSystemPrompt(session);
    
    return { messages, systemPrompt, cwd: session.cwd };
  }
  
  private buildSystemPrompt(session: Session): string {
    return `你正在恢复一个旧会话(ID: ${session.id},最后活跃于 ${new Date(session.lastActiveAt)})。
    以下是该会话的历史消息。请接着这个上下文继续回答,就像刚才还在聊天一样。
    如果用户询问之前的内容,你可以引用历史。`;
  }
}

3. 分支魔法(Branching)

Claude Code CLI 最酷的功能之一:会话分支。假如你从一个旧会话恢复,然后聊了新的内容,系统会创建一个新会话,但标记 parentSessionId 指向旧的。这样你就有了一棵对话树——就像平行宇宙!

# 示例会话树
sess_001 (初始对话)
  ├─ sess_002 (从 001 /resume 后继续)
  └─ sess_003 (又从 001 /resume,但走了另一条路)

这让你可以回到某个决策点,尝试不同的 AI 建议,而不丢失任何分支。


⏱️ 时序图:/resume 的完整调用过程

resume .png


🧙 最佳用法:小克给你的六条锦囊

1. 给会话起个好名字

不要默认用 会话 2024-03-15 14:30:22。用 /title 命令改名,比如“支付接口幂等性设计”。以后 /resume 一眼就能找到。

2. 频繁保存关键节点

遇到复杂 bug 或设计决策前,手动触发 /save(如果有)或直接 /resume 后再 /branch。分支就是你的“存档点”。

3. 利用恢复来“热启动”

早上上班,直接 /resume 昨天的会话,不用重新描述项目背景。省下 10 分钟上下文 token。

4. 清理无用会话

用 /sessions prune(如果支持)或手动删除 ~/.claude/sessions/ 下的旧文件。否则会像衣柜一样爆满。

5. 恢复后验证环境

如果之前你让 AI 改了 config.js,恢复会话时,Claude Code 不会自动撤销文件修改。建议在恢复后手动 git diff 确认状态。或者使用内置的文件快照功能(如果开启)。

6. 分支实验

遇到不确定的方案:

当前会话: “方案A 用 Redis”
/resume 回到选择前的会话
然后说 “试试方案B 用 Memcached”

这样你就有两个分支,可以分别对比 AI 的建议。


🎯 真实代码示例:手动实现一个极简版 /resume

假设你正在写一个自己的 CLI 工具,想模拟这个行为:

#!/usr/bin/env python3
import json, os, sys
from datetime import datetime
from pathlib import Path

SESSIONS_DIR = Path.home() / ".my_cli_sessions"
SESSIONS_DIR.mkdir(exist_ok=True)

class SessionManager:
    def __init__(self):
        self.current_session = None
        self.messages = []
    
    def new_session(self, title=None):
        sess_id = f"sess_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
        title = title or f"会话 {sess_id}"
        self.current_session = {"id": sess_id, "title": title, "messages": []}
        self.messages = []
        self._save()
        return sess_id
    
    def _save(self):
        if self.current_session:
            path = SESSIONS_DIR / f"{self.current_session['id']}.json"
            with open(path, 'w') as f:
                json.dump(self.current_session, f, indent=2)
    
    def list_sessions(self):
        sessions = []
        for p in SESSIONS_DIR.glob("*.json"):
            with open(p) as f:
                data = json.load(f)
                sessions.append((data['id'], data['title'], len(data['messages'])))
        return sessions
    
    def resume(self, sess_id):
        path = SESSIONS_DIR / f"{sess_id}.json"
        if not path.exists():
            print("会话不存在")
            return False
        with open(path) as f:
            self.current_session = json.load(f)
        self.messages = self.current_session['messages']
        print(f"✅ 已恢复 '{self.current_session['title']}',共 {len(self.messages)} 条消息")
        # 打印最后几条历史作为上下文提醒
        for msg in self.messages[-3:]:
            print(f"[{msg['role']}]: {msg['content'][:50]}...")
        return True
    
    def add_message(self, role, content):
        msg = {"role": role, "content": content, "timestamp": datetime.now().isoformat()}
        self.messages.append(msg)
        if self.current_session:
            self.current_session['messages'] = self.messages
            self.current_session['lastActiveAt'] = datetime.now().isoformat()
            self._save()
    
    def chat_loop(self):
        if not self.current_session:
            self.new_session()
        print("进入对话模式 (输入 /resume 列出会话, /exit 退出)")
        while True:
            user_input = input("\n> ")
            if user_input == "/exit":
                break
            elif user_input == "/resume":
                sessions = self.list_sessions()
                print("\n可恢复的会话:")
                for idx, (sid, title, cnt) in enumerate(sessions):
                    print(f"{idx+1}. {title} ({cnt}条消息) [{sid}]")
                choice = input("请输入编号: ")
                try:
                    chosen = sessions[int(choice)-1][0]
                    self.resume(chosen)
                except:
                    print("无效选择")
                continue
            # 正常消息
            self.add_message("user", user_input)
            # 这里本该调用 AI API,我们模拟 AI 回复
            ai_reply = f"小克收到: {user_input} (基于 {len(self.messages)} 条历史)"
            print(f"🤖 {ai_reply}")
            self.add_message("assistant", ai_reply)

if __name__ == "__main__":
    sm = SessionManager()
    sm.chat_loop()

运行一下:

$ python mini_resume.py
进入对话模式 (输入 /resume 列出会话, /exit 退出)

> 帮我写个排序算法
🤖 小克收到: 帮我写个排序算法 (基于 2 条历史)

> /resume
可恢复的会话:
1. 会话 sess_20240315_151022 (2条消息) [sess_20240315_151022]

请输入编号: 1
✅ 已恢复 '会话 sess_20240315_151022',共 2 条消息
[user]: 帮我写个排序算法...
[assistant]: 小克收到: 帮我写个排序算法...

🌟 总结:为什么要爱 /resume

  • 省 token:不用重复喂上下文,Claude 直接“记起来”。
  • 省时间:不用重新描述项目结构、报错信息。
  • 可回溯:犯错后可以回到之前的某个状态,重新尝试。
  • 分支探索:并行试验多个 AI 方案,互不干扰。

现在,你已经是 /resume 的魔法学徒了。快去 Claude Code CLI 里输入 /resume,看看你之前留下的那些“日记本”吧!📓✨