ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

context-mode实战:用bash函数实现工作现场一键切换

context-mode实战:用bash函数实现工作现场一键切换 最近身边好几个朋友都在搜 context-mode有的以为它是某款编辑器里的新按钮有的把它当成大模型产品里的“记忆模式”。这词确实被用得很杂但我自己的理解非常具体context-mode 是一套“工作现场切换机制”。我靠它解决了两个特别现实的麻烦——多任务并行时状态不断丢失以及给 AI 编程助手交代背景时说话太啰嗦。如果你也经常一上午切换五个分支、开着三个终端、还离不开 AI 辅助写代码这篇文章应该对你有用。我不是在鼓吹什么复杂框架。相反我用一个不到两百行的 bash 函数搭了一套叫ctx的 context-mode 工具用了一年之后它已经成了比git checkout还频繁的命令。下面我会先讲清楚为什么这个问题的核心不是“记录”而是“切换”然后给出完整可抄的实现再分享和 AI 编程配合的工作流最后聊聊哪些功能我后来亲手砍掉了。1. 两次线上事故让我重新审视“上下文”这三个字我真正开始认真思考 context-mode是在同一天搞砸两件事之后。早上从后端分支切到前端分支忘了导出某个环境变量本地测试直接跑出一片红。下午想找半天前贴给 AI 助手的一份需求记录结果只找到一句“按刚才说的就行”。问题当然有我记性差的成分但根子在于我从来没有认真管理过“当前工作的上下文”。1.1 “上下文”到底是什么这里的上下文不是聊天窗口里那段对话记录而是你完成一个任务时大脑和工作台面上所有相关信息的集合。对写代码这件事来说至少包括四样东西当前所在的工作目录这个项目特有的环境变量任务备注、待办、已知坑最近的 git 状态和几条关键命令。这四样东西一旦散掉你就必须靠回忆、翻历史、读文档把它们重新拼起来。而所谓 context-mode说白了就是把“这四样东西”打包成一个可命名、可存取、可一键恢复的状态单元。我把它叫作“现场”——一个现场就是一个上下文。我后来意识到用办公室桌面来类比特别好理解程序员的工作台是虚拟的但我们和整理桌面一样不可能把五千个文件全摊开。你需要的是为了完成“写登录模块”这个任务桌面上刚好摆着相关代码、数据库连接串、TODO 列表。任务一换桌面也跟着换。context-mode 做的就是“一键换桌面”的动作。1.2 为什么传统工具都差一口气其实早就有很多工具想解决这个问题。终端复用器能保存窗口布局IDE 工作区能记住打开的文件shell 历史能翻出之前敲过的命令。但真的用起来每种都差那么一点工具类型它擅长什么它不擅长什么终端复用器保存窗口、面板、进程不理解“当前任务”只理解“当前窗口”IDE 工作区记住打开的文件和视图不保存环境变量跨项目切换笨重shell 历史记录所有命令噪音极多找一条能用的要靠 grep云剪贴板同步文本片段没有结构过几天就变成乱坟岗这些工具解决的是“工作面”的某一块但 context-mode 解决的是“工作面之间的关系”。它的核心动作不是记录全部而是切换状态保存当前现场加载目标现场。这个思想上的差别比具体功能重要得多。2. context-mode 不是会话录制器而是状态切换器确定要自己写一个 context-mode 之后我最初的想法特别贪婪能不能把当前终端所有状态都存下来下次切换之后完美恢复试了两天我就放弃了。全量保存听起来很美实际上是在制造新的混乱。2.1 最小化原则保存越少恢复越快我把需要保存的信息严格限制在“没有它任务就进行不下去”的范围内。工作目录是必须的没有它你连代码在哪儿都不知道白名单环境变量是必须的因为不同项目的 API 地址、调试开关、连接串往往不一样备注文件是必须的这是对抗记忆衰减的唯一武器git 状态不是必须保存而是在需要看的时候现查现用。剩下的东西我全都拒绝保存。命令历史不保存因为 90% 的命令是重复劳动完整环境变量不保存因为从系统环境到 shell 内置变量切过去准会污染下一个项目。我给自己立了一条规矩一个 context 只包含“能用一个文件描述清楚”的状态。如果你需要 50 行的环境变量才能恢复工作说明项目本身的管理有问题。2.2 用“保存”和“切换”两个原语理解一切整个 context-mode 用两个原语就能描述清楚。第一个是 save把当前现场的快照写入当前上下文第二个是 switch先自动执行一次 save再把目标上下文的快照加载进来。注意 switch 里面藏着一次默认 save这是最关键的细节。很多人切换任务时会想“我记得这个事情做完了不用保存了。”事实恰恰相反任务切换的瞬间之前的现场是最完整也最容易丢失的。与其依赖你那不靠谱的记忆不如让每次切换都强制落盘。这就好比下班前你总得把办公桌稍微收拾一下否则第二天来你会面对一座废墟。我做了这样的设计执行ctx use backend-fix的时候工具会先把当前frontend现场保存到frontend名下再加载backend-fix。这个“先存后换”的默认行为让状态丢失的概率几乎降到了零。为了极端场景也可以显式加参数跳过保存但我在实际使用中一次都没跳过。2.3 为什么必须做成 shell 函数而不是独立脚本这是实现里面第一个大坑。如果我把ctx写成一个普通的独立脚本放在/usr/local/bin/ctx那它会有个致命缺陷脚本运行在子进程里在脚本里面执行cd和export只会改变子进程自己的状态根本影响不到你的当前 shell。你会看到脚本执行成功但目录纹丝不动。所以 context-mode 的实现必须是一个定义在当前 shell 里的函数或者通过eval $(ctx use xxx)的方式间接执行。我选择了函数方案因为最直接、最好调试。你把一段函数粘贴到~/.bashrc或~/.zshrc重载配置之后ctx就成了 shell 内置操作。这也是整套工具能用得顺手的前提。3. 可抄作业的 bash 实现把 ctx 函数写进 shell最实用的部分来了。我下面给出的是一份完整可运行的 shell 函数在 bash 和 zsh 里都测过。你不需要安装任何依赖不需要 Python不需要 Node环境里只需要一个 POSIX 风格的基础工具集。3.1 目录结构三个文件撑起一个现场每一个上下文在~/.context-mode/contexts/名字目录下包含三个文件~/.context-mode/ contexts/ frontend/ pwd # 记录这个现场对应的工作目录 env # 白名单环境变量每行 KEYvalue notes.md # 任务备注Markdown 格式随便写 user-api/ pwd env notes.md current # 一个文本文件记录当前上下文的名字current是整个系统的指针。任何操作第一个动作都是先读它知道“我现在在哪个现场”。目录结构刻意保持简单这样备份、迁移、用 grep 查内容都特别方便。3.2 核心函数直接贴进 .zshrc 就能用我平时用的是下面这份精简版去掉了花哨的报错和颜色只保留骨架逻辑。你可以把它整个复制到~/.zshrcbash 用户放~/.bashrc然后source ~/.zshrc就能跑起来。export CONTEXT_MODE_HOME${CONTEXT_MODE_HOME:-$HOME/.context-mode} ctx() { local cmd$1 shift 2/dev/null || true local base$CONTEXT_MODE_HOME local contexts$base/contexts local current_file$base/current local current mkdir -p $contexts if [[ -f $current_file ]]; then current$(cat $current_file) else currentplaceholder fi _ctx_save() { local dir$contexts/$1 mkdir -p $dir pwd $dir/pwd env | grep -E ^(APP_|PROJECT_|DB_|DATABASE_|NODE_ENV|DEBUG|FLAG_) $dir/env # 防止 env 文件为空导致后续读取报错 : $dir/env } _ctx_load() { local dir$contexts/$1 [[ -d $dir ]] || { echo unknown context: $1; return 1; } local workdir workdir$(cat $dir/pwd 2/dev/null || echo $HOME) if [[ -d $workdir ]]; then cd $workdir else echo warn: workdir $workdir missing, stay where you are fi while IFS read -r line; do [[ -z $line || $line \#* ]] continue export $line 2/dev/null || true done $dir/env echo $1 $current_file if [[ -f $dir/notes.md ]]; then echo notes for [$1] sed -n 1,12p $dir/notes.md fi } case $cmd in init) mkdir -p $contexts/$1 echo # notes for [$1] $contexts/$1/notes.md _ctx_save $current echo $1 $current_file _ctx_load $1 ;; use) _ctx_save $current _ctx_load $1 ;; save) _ctx_save $current echo context [$current] saved ;; list) for d in $contexts/*/; do echo $(basename $d) done ;; brief) local name${1:-$current} local dir$contexts/$name echo ## context: $name echo ### workdir cat $dir/pwd 2/dev/null || echo $HOME echo ### git local wd wd$(cat $dir/pwd 2/dev/null || echo $HOME) if [[ -d $wd/.git || -n $(find $wd -maxdepth 1 -name .git -type d 2/dev/null) ]]; then (cd $wd git branch --show-current 2/dev/null) (cd $wd git status --short 2/dev/null | head -n 12) else echo (no git repo) fi echo ### notes sed -n 1,50p $dir/notes.md ;; *) echo usage: ctx init|use|save|list|brief [name] ;; esac }几个实现细节我单独说一下。env | grep -E ^(APP_|PROJECT_|...)是白名单机制只保存前缀匹配的变量这是防止上下文互相污染的关键。_ctx_load里的export $line会让环境变量直接进入当前 shell 进程这正是函数方案优于独立脚本的地方。cd必须在函数体里直接执行不需要eval阅读和维护起来都很轻松。3.3 日常操作的肌肉记忆工具本身很简单复杂的是养成习惯。我给自己定的规矩是打开项目开始新任务时ctx init frontend工作告一段落ctx save换任务时ctx use user-api;想不起来某个现场是干嘛的ctx brief user-api。我没有给每个动作设置更短的别名因为ctx本身已经足够短了。如果你想把切换做得更快可以在 shell 配置里加上alias cctx手快的自然懂。4. 一份完整工作流从改前端样式到顺手修接口说了这么多不如完整推演一个半天的真实工作流。这是我用 context-mode 之后非常典型的一天。4.1 上午先进入“前端组件重写”现场早上到工位我先创建今天的第一个上下文cd ~/work/company/web ctx init frontend这一步做的事情是记住了当前目录导出了当前的白名单环境变量然后在我脑袋还清楚的时候打开notes.md写下今天的任务“把用户列表虚拟滚动做出来注意接口分页字段是 offset 不是 page”。接下来一整个上午我在这一个现场里改代码、跑构建、查样式。中途同事来找我问一个线上 bug 的数据结构我必须切去另一边的仓库。我顺手先敲了一条ctx save其实不敲也没关系因为我马上要用的ctx use会自动保存。真正有用的习惯是在保存之前我会快速往notes.md里补一句“虚拟滚动实现了一半卡在动态高度测量”。这是整个系统里最值钱的几秒钟。4.2 下午切到“用户接口修复”现场带着那行备注我执行ctx use user-api函数自动做了三件事把frontend现场的 pwd、env、notes 全部保存到对应目录读取user-api现场把工作目录切到接口服务仓库把数据库调试开关导出来在终端顶部打印出user-api的前 12 行备注。整个过程大概几毫秒视觉上只看到提示文字一闪而过。于是我的终端瞬间切换成了另一个完整的工作台甚至不需要再想起“这个项目用的是哪个目录、要不要导出DEBUG”。处理完线上问题我又执行ctx use frontend回到上午那个写到一半的虚拟滚动备注清楚地提醒我卡在哪里。这一天我至少来回切了八次没有一次需要重新回忆“我刚在干什么”。4.3 现场切换对比传统方式 vs ctx 方式环节传统方式ctx 方式换项目先记着当前代码状态手动 cdctx use 项目名自动切换环境变量翻.env文件手动 export白名单变量自动加载换任务备忘开个临时笔记可能找不到notes.md 永久跟随现场回来继续靠记忆和 git diff看 notes 前 12 行立刻回神有人会说我用 IDE 也能做到。但 IDE 的问题是同时只对一个项目友好而如果你的工作流经常要跨仓库执行命令、改配置、跑测试终端里的 context-mode 就比 IDE 重很多。它不是替代 IDE而是给终端负责的那部分补齐了“状态切换”能力。5. 把 context-mode 的产出喂给 AI 编程助手这两年写代码离不开 AI 辅助之后我发现最浪费时间的动作就是“给 AI 讲背景”。问一句“帮我看看这个报错”你得先把项目结构、报错上下文、改动文件、你尝试过什么全部贴上去。有了 context-mode这件事可以变得非常机械。5.1 一条 ctx brief 就能生成的上下文摘要我给ctx brief设计的初衷就是让它成为“给另一个智能体快速交接”的标准格式。运行之后你会得到类似这样的内容## context: user-api ### workdir /Users/me/work/service ### git fix/timeout-retry M internal/handler/order.go ### notes 需求order 超时后增加重试 已经完成 handler 的骨架 还没写 mq 消费 part幂等键用 order_id这四条信息恰好覆盖了 AI 对话最需要的东西项目在哪、当前分支、改了哪些文件、任务干到哪一步。你不需要写长篇大论因为这些内容是从现场自动提取的。5.2 粘贴给 AI 的正确姿势我的标准动作是先ctx brief user-api复制输出然后在 AI 对话框里附上下面这段模板这是我现在的工作上下文 [粘贴 ctx brief 的输出] 请基于这个上下文回答我的问题。如果信息不足先列你需要补充的内容不要编造文件路径。这个模板看起来不起眼但它把 AI 的想象空间限制住了。没有上下文时AI 会给出非常泛的通用答案有了上下文它至少能准确说出“你的order.go里已经有什么”而不是胡编。这里有个技巧brief 输出最好保持短小控制在 30 行以内。如果 notes.md 写太长AI 会被无关信息干扰反而抓不住重点。5.3 更进一步写一个 ctx-ai 辅助函数如果你每天要和 AI 对话很多次可以再加一个函数把整个动作变成一条命令ctx-ai() { ctx brief ${1:-} /tmp/ctx_brief.md echo context brief saved to /tmp/ctx_brief.md echo --- cat /tmp/ctx_brief.md }如果你使用的 AI 工具支持文件引用把/tmp/ctx_brief.md作为附件传进去最干净。如果不支持直接 CtrlV 粘贴输出也完全能用。我通常会在写周报或者 code review 前对每个上下文跑一次ctx-ai相当于给自己生成一份“这周我干了什么”的草稿。5.4 为什么这种方式比手动描述可靠手动描述项目背景最大的问题是“你以为你和 AI 在同一个频道上”。你可能会漏掉刚切换过分支也可能会忘记之前已经让 AI 做过一次重构结果它又给你同一套方案。context-mode 保存的是机器可读、可回溯的状态notes.md 里甚至记录了上一次决策的结论。AI 拿到的不是你的记忆而是现场的快照这点差别是决定性的。6. 用了一年后我去掉了这些“锦上添花”工具做出来总会忍不住加功能。我在 context-mode 上吃过不少教训也亲手砍掉过好几个看起来很诱人的设计。这一节算是一个“反功能清单”希望能帮你少走弯路。6.1 砍掉自动记录完整命令历史第一版我会在ctx save时把最近几十条历史命令拼到 notes 后面想着这样能复盘。实际用了两天就发现历史里塞满了pnpm dev、git diff、cat package.json这些毫无营养的重复命令notes 文件迅速膨胀到几百行再也没人想看。最后我把这个功能整个删掉只保留手动写备注。手动写备注虽然懒但是每一句话都是经过提炼的信息价值远高于自动记录的流水账。6.2 砍掉“自动切换上下文”我试过监听chpwd每次 cd 到一个不同项目目录就自动切换上下文。听起来非常智能实际体验是灾难频繁误切、状态错乱、notes 到处乱飞。context 切换本质上是一个“有意识的行为”它对应的是你大脑里“现在要换任务”的决策。如果把这种决策交给目录名去猜测系统就会变得捉摸不透。后来我只保留了手动ctx use也是在逼自己每次切换前想清楚我是不是真的要放下手头这件事了。6.3 永远不要把密钥写进 env 文件这是安全红线。虽然白名单只保存DB_、API_这类前缀但如果你的开发环境里把真实生产密钥直接放在了这些变量里ctx save会把它原样写到~/.context-mode/contexts/xxx/env。我的建议是env 文件保存的只能是“指向密钥的引用”比如DATABASE_URL_FILE/run/secrets/xxx这种间接方式而不是密钥本身。同时要给~/.context-mode设置目录权限为 700降低文件泄露风险。6.4 多个终端窗口时不要共享同一个 current 指针current文件是全局的意味着你开三个终端窗口它们看到的“当前上下文”都是同一个。这本身没问题但如果你在窗口 A 执行了ctx use frontend窗口 B 再敲ctx save保存的就是 frontend 的现场而不是它心里的那个现场。最初我为此纠结了很久后来想通了同一个时间人其实只能有“一个主要注意力现场”。多窗口时我只会在一个主终端里做切换其余窗口仅用于临时执行命令。如果你确实需要真正的多现场并行那就得给每个终端加独立标识这个复杂度目前对我来说不值得。6.5 定期清理不用的上下文context 和桌面快捷方式一样创建太多就会失效。我的.context-mode/contexts里现在有 12 个现场其中大概只有 5 个是活跃的。每个月清理一次把已经交付的任务直接删除目录保持整个环境干净。rm -rf ~/.context-mode/contexts/old-task这样的操作虽然简单粗暴但在 context-mode 里就是最正确的清理方式。最后再分享一个小技巧我每天开机后的第一件事永远是打开终端跑一遍ctx list然后从列表里选一个主线任务进入。这比从浏览器书签里找项目、再从终端翻历史要顺畅得多。context-mode 的终极价值不是帮你存了多少东西而是让“开始工作”和“切换工作”变成一种不需要思考的肌肉记忆。如果你手上也有多项目并行、频繁搭 AI 对话的烦恼不妨把这套函数复制下去改一改前缀规则然后坚持用两周你会回来感谢那个在 notes.md 里随手写两行备注的自己。
返回列表