ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

context-mode:用tmux和AI上下文把多项目切换压缩到1分钟

2026/10/8 16:20:15 拓冰建站 浏览量
context-mode:用tmux和AI上下文把多项目切换压缩到1分钟 1. 先说我为什么会被上下文切换逼疯如果你跟我一样手头同时压着两三个项目那下面这个场景你一定不陌生上午还在写 Python 后端接口下午要切到 React 项目调样式晚上可能还要打开博客写点东西。每次切换都不是单纯换个文件夹那么简单而是要重新记起这个项目用哪个端口启动、依赖装在哪个版本管理器里、代码风格是 tab 还是空格、Lint 规则有哪些例外、测试命令是 pytest 还是 pnpm test……这些琐碎信息全部堆在脑子里重新过一遍状态才能回满。我实测过一个人从 A 任务切到 B 任务最顺利的情况下也需要 15 到 20 分钟才能恢复到心流水准如果切完发现环境还没配好半小时就直接浪费了。后来我给自己定了一个目标把这种切换后的重新回忆压缩到 1 分钟以内。也就是说打开项目、进入状态、开干三个动作必须一气呵成。围绕这个目标做的整套方案我给它起了个名字叫 context-mode也就是上下文模式。核心思路很简单不靠脑子记上下文而是把上下文固化成可以随时加载的模式。说白了context-mode 不是什么高深算法就是一套环境 工具 提示词的组合打包方案。工程上有一句老话叫约定优于配置context-mode 做的就是类似的事把每个项目、每类任务需要的所有隐性信息显式地写进一个模式配置里然后让 shell、编辑器、AI 助手在进入对应模式时自动加载。这篇文章里我把整套做法的原理、脚本、配置和踩过的坑全部摊开讲适合那些正在被多项目切换折磨的开发者参考。2. 我的 context-mode 架构长什么样三层分离动手写脚本之前我先把问题拆了一遍。一个人要真正进入状态其实需要三层上下文同时就位环境层终端会话、项目目录、环境变量、启动命令。这层没准备好你连 dev server 都起不来。工具层编辑器里打开的缓冲区、缩进规则、LSP语言服务器选择、格式化工具。这层没准备好你写的每一行代码都可能被 format 得跟你预期不一样。协作层给 AI 助手的项目背景说明、代码规范、当前任务目标。这层没准备好AI 的回答永远是泛泛而谈的通用建议。我最初只搞了环境层命令能用就以为大功告成。用了一周发现终端是切过去了但 Neovim 里没有这个项目的自定义配置AI 助手也不知道这个项目的技术栈等于只恢复了三分之一的状态。所以后来我把方案调整成三层各自维护一套配置关键词是模式文件。每个项目或者说每类任务在根目录下放一个隐藏文件.ctxrc内容长这样name: blog root: ~/dev/blog session: blog shell: zsh nvim_workspace: true env: NODE_ENV: development BLOG_PORT: 5252 ctx_file: .ctx/context.md这个文件就是模式的定义。name是模式的别名root是项目根目录session表示我要挂靠的 tmux 会话名env是这个模式下必须存在的环境变量ctx_file是给 AI 助手看的项目上下文说明文件。下文所有脚本和配置都是围绕解析这个.ctxrc来做的。我用了一个非常古老的类比来跟同事解释这套东西它就像家里的情景灯光。你可以设置观影模式把主灯关掉只留落地灯也可以设置阅读模式把书桌灯调亮。模式切换之后整屋的光环境一起变化而不是你一个个去拧开关。context-mode 也一样进入某个项目终端、编辑器、AI 提示词全部跟随模式自动切换不需要你一条条自己配置。3. 环境层实操tmux 会话自动恢复脚本环境层我选 tmux 作为核心载体没有用 docker 也没用虚拟机。原因很简单开发者日常工作流里tmux 是绝大多数人都能零成本上手的东西而且会话分离、重连的功能天然适合模式恢复。哪怕你是 VS Code 党终端窗口里跑一个 tmux 也完全不冲突。3.1 脚本整体逻辑脚本总共做了四件事对应下面几个关键步骤用find在当前项目目录我统一放在~/dev下搜索所有的.ctxrc文件把这些模式列出来。用fzf交互式选择要进入哪个模式。解析.ctxrc的session和root字段判断对应 tmux 会话是否已存在不存在就创建存在就直接附加。在会话里设置环境变量并自动执行启动命令比如打开 Neovim。完整脚本我贴出来注释写得很细#!/usr/bin/env bash # ctx-switch.sh - 加载 context-mode 环境层 DEV_ROOT$HOME/dev RCFILE.ctxrc # 第一步找出所有模式配置 mapfile -t ctx_files (find $DEV_ROOT -maxdepth 3 -name $RCFILE 2/dev/null) if [ ${#ctx_files[]} -eq 0 ]; then echo 没有找到任何 .ctxrc 模式配置请先在项目目录创建。 2 exit 1 fi # 第二步用 fzf 做选择展示模式名称和所在路径 selected_file$( for f in ${ctx_files[]}; do name$(grep ^name: $f | head -1 | awk {print $2}) dir$(dirname $f) echo $name $dir done | fzf --prompt选择要进入的 context-mode: --with-nth1 ) if [ -z $selected_file ]; then echo 未选择任何模式直接退出。 exit 0 fi # 由显示文本反查配置文件路径 selected_name${selected_file%% *} target_dir$DEV_ROOT/$selected_name # 第三步读取 .ctxrc 字段 root$(grep ^root: $target_dir/$RCFILE | awk {print $2}) session$(grep ^session: $target_dir/$RCFILE | awk {print $2}) cmdline$(grep ^cmd: $target_dir/$RCFILE | tail -1 | cut -d: -f2-) root${root/#\~/$HOME} session${session:-${selected_name}} # 第四步tmux 会话不存在则新建存在则直接附加 if ! tmux has-session -t $session 2/dev/null; then tmux new-session -d -s $session -c $root # 按模式注入环境变量 while IFS: read -r key val; do if [ -n $key ]; then tmux set-environment -t $session $key $val fi done (grep -A99 ^env: $target_dir/$RCFILE | grep -E ^\s[A-Z_]: | tr -d ) fi tmux switch-client -t $session # 如果配置了启动命令执行它 if [ -n $cmdline ]; then tmux send-keys -t $session $cmdline Enter fi几个细节我特别想说明一下为什么用grep awk而不是source加载.ctxrc因为.ctxrc是配置不是脚本如果里面有 shell 特有的语法source会造成意外副作用比如把未转义的特殊字符当成命令执行而grep是按行解析更可控。再者我故意让模式名称用目录名而不是把名字单独抽出来这样find反查路径非常直观不容易出错。3.2 这个脚本帮我省掉了哪些重复动作以前我每次切到博客项目都要手动执行这么一串cd ~/dev/blog nvm use 18 export NODE_ENVdevelopment tmux new -s blog nvim .如果中间漏了nvm use 18启动的时候 node 版本不对报错还要现排查。现在脚本把这些全部收敛成一条指令ctx-switch。进去以后目录、node 版本、环境变量、tmux 会话名称全部就位直接开写。还有一个很实用的点tmux 会话和后台进程的关系。实际开发的时候我不会傻乎乎地所有事情都在前台跑经常是开一个窗口跑 dev server另一个窗口写代码。tmux new-session -d之后会话没有前台附加dev server 不会因为终端关掉被杀掉下次重新进入 context-mode直接附加到原来的会话所有后台任务都还在无缝衔接。这一点对远程开发尤其重要——我从家里 SSH 到服务器上切回公司电脑再 SSH 上去tmux attach一眼恢复昨天的战场。4. 工具层实操编辑器跟随模式自动换配置环境层解决了我在哪的问题接下来编辑器要解决我该怎么写的问题。编辑器这一层我花了最多时间打磨因为每个项目对缩进、格式化、语言服务的要求差异真的很大。4.1 Neovim 自动配置的触发机制我用 Neovim 配了 Lua 逻辑核心是一个自动命令autocmd监听两个事件BufEnter进入缓冲区和DirChanged目录切换。每次触发时从当前文件路径向上回溯找到最近的.ctxrc然后执行模式对应的编辑器设置。-- ~/.config/nvim/lua/ctx-mode.lua local function find_ctxrc_dir(start) local dir vim.fn.fnamemodify(start, :p:h) while dir ~ vim.fn.fnamemodify(dir, :h) do if vim.fn.filereadable(dir .. /.ctxrc) 1 then return dir end dir vim.fn.fnamemodify(dir, :h) end return nil end local function set_mode_from_dir(dir) if not dir then return end -- 读取 .ctxrc 中的 tool 字段这里用简单的键值解析 local tool vim.fn.system(grep ^tool: .. dir .. /.ctxrc | awk {print $2}):gsub(%s, ) if tool go then vim.bo.expandtab false vim.bo.shiftwidth 4 vim.bo.tabstop 4 vim.g.go_fmt_autosave 1 elseif tool js then vim.bo.expandtab true vim.bo.shiftwidth 2 vim.bo.tabstop 2 end -- 可选动态切换 LSP 配置 local lsp_name vim.fn.system(grep ^lsp: .. dir .. /.ctxrc | awk {print $2}):gsub(%s, ) if lsp_name and lsp_name ~ then vim.lsp.stop_client(vim.lsp.get_clients()) -- 这里按 lsp_name 拉起对应 server细节略 end end vim.api.nvim_create_autocmd({ BufEnter, DirChanged }, { callback function(args) local dir find_ctxrc_dir(vim.fn.expand(%:p)) set_mode_from_dir(dir) end, })这段配置真正解决问题的地方在于向上回溯。我们在 monorepo 里打开一个包内的文件时项目的.ctxrc不在当前位置而在多个父目录之上。回溯到最近的配置是避免用错模式的关键。我专门测试过嵌套目录的情况——在packages/web下打开文件能正确落到web的模式配置上如果web本身没有.ctxrc那就继续向上直到仓库根目录。逻辑虽然简单但顺序必须正确先找最近的再一层层往外退否则很容易被子目录里的残留配置带偏。4.2 editorconfig 和 direnv 是天然的盟友说实话单独用 Neovim 脚本管缩进远不如把这一类约定写到.editorconfig里干净。我的方案是两者结合.editorconfig管风格约定Neovim 脚本管编辑体验。风格约定是给所有用这个项目的人看的哪怕他不用 Neovim用 VS Code 也能识别编辑体验是给我自己用的自动切换 LSP、启动代码补全引擎等等。环境变量那部分则直接用direnv接管。.ctxrc里写的env.NODE_ENVdevelopment我会同步在.envrc里写一份export NODE_ENVdevelopment export BLOG_PORT5252direnv会在你 cd 进目录的那一刻自动加载这些变量退出目录自动卸载。tmux 里如果开了 direnv 插件连新建窗口都会自动继承。这一个组合拳打下来模式环境完全不需要我手动 remember 了。编辑器和环境变量的联动是我认为整个 context-mode 方案里最容易被低估的环节。很多人只做了 tmux 会话恢复结果倒了环境层漏了工具层切过去之后缩进乱了、LSP 没起来体验比不用这套方案还差。所以我把工具层放在跟环境层同等重要的位置宁可脚本少写点也要把编辑器自动配置和direnv的加载做扎实。5. 协作层实操给 AI 助手配一个固定上下文窗口第三个层次现在越来越重要了AI 协作时的上下文。我平时会让 AI 帮我生成代码、排查报错、写测试但很早就发现一个问题——如果我不把项目背景告诉它它答出来的东西基本是模板级别的。比如我让它给这个 API 加一个限流中间件它不知道我们用的是什么框架、什么语言、项目里有没有现成的限流库、代码风格是函数式还是面向对象。每次都要在对话里重新解释一遍既费时间又容易越说越乱。我的解法是在项目里维护一个.ctx/context.md文件内容包含三块项目一句话介绍和技术栈清单目录结构说明和关键模块职责常用命令、代码风格约定、历史决策原因然后写一个脚本把这份文件自动注入到我要发给 AI 的 prompt 里。我的算法是这样的#!/usr/bin/env bash # ai-ask.sh - 组装带上下文的问题 context_file # 从当前目录逐层向上寻找 .ctx/context.md dir$(pwd) while [ $dir ! / ]; do if [ -f $dir/.ctx/context.md ]; then context_file$dir/.ctx/context.md break fi dir$(dirname $dir) done if [ -n $context_file ]; then cat /tmp/ctx_prompt.md EOF 请基于以下项目上下文回答我的问题。 # 项目上下文来自 $context_file $(cat $context_file) # 我的问题 $* EOF # 这里可以根据你的 AI 工具替换命令比如 claude、opencode或者直接复制到剪贴板 command$(cat /tmp/ctx_prompt.md) echo $command | pbcopy 2/dev/null || echo $command echo --- 上下文已组合并复制 --- fi这个脚本最核心的价值不在于它多复杂而在于它把每次都要人工告诉 AI 的信息变成了项目目录里显式维护的文本。你只需要把上下文文件写好之后每次提问脚本自动带上项目背景。实测下来AI 给出的代码在风格匹配、依赖选择上的准确度提升非常明显尤其体现在它知道我们用的是 pnpm 还是 npm给出的安装命令不会跑偏它知道项目的目录约定接口文件放在api/而不是乱建议新目录它知道项目里已有的工具函数会主动复用而不是重复发明轮子我也试过直接把上下文文件固定放在~/ctx/base.md里不区分项目结果效果大打折扣。原因很简单不同项目的上下文差异太大通用模板只能覆盖 20% 的信息剩下 80% 还得靠单项目维护。所以后来我一直坚持每个项目一份 context 文件这个原则。6. 用了一个月后我要说几个特大坑再好的方案落地过才能知道哪里会翻车。我这套 context-mode 用了一个多月踩了几个坑有的修复了有的还留着都值得说一下。6.1 坑一环境变量泄漏最早我写 tmux 脚本时没有用tmux set-environment而是直接在 attach 之后export变量。问题来了tmux 会话里的环境变量是会话级的如果你设置了NODE_ENVproduction切到另一个不带这个变量的模式残留的NODE_ENVproduction会继续污染后续所有命令。最典型的翻车现场是我从博客项目切到另一个项目跑测试结果测试环境莫名其妙变成了生产环境排查了半天发现是旧会话里残留的环境变量在作祟。修这个问题的思路是双管齐下一是所有环境变量必须通过tmux set-environment -t 会话名来设置这样它只属于特定会话二是在切换到新模式时主动清理会话里旧的环境变量脚本里加一段tmux set-environment -u NODE_ENV tmux set-environment -u BLOG_PORT从今往后每个会话只保留自己模式的环境不跨模式混用。6.2 坑二模式数量失控刚开始搭这套系统的时候我像收集癖一样给每个小目录都配了.ctxrc从日常开发到临时脚本前前后后配了十几个模式。结果用了一周发现效率反而下降了ctx-switch的选择列表太长fzf 模糊匹配反而增加了决策成本而且模式之间的配置经常相互干扰。后来我给自己下了一条铁律同时保持活跃的模式绝不超过 5 个。不是项目的数量限制而是并行工作的模式限制。手头只有 2、3 个活跃项目就把模式精简到这几个其余的等真正需要时再加不提前铺开。这条铁律让模式维护成本降到了近乎为零切换体验才真正回归到快。6.3 坑三编辑器配置的幽灵回退Neovim 脚本里我用BufEnter监听但有个头疼的场景用 Leader 快捷键随意切换 buffer 时BufEnter高频触发每次都要向上回溯寻找.ctxrc如果当前文件不在任何项目目录下比如打开了一个/tmp/test.js就会匹配不到模式编辑器回到默认配置。这本是合理行为但有时用户打开的一批缓冲区混合了项目和临时文件切来切去时配置会在模式 A和默认模式之间反复横跳视觉上非常烦人。我的妥协方案是只有当前目录确实都找不到.ctxrc时才用默认配置而且设置一个标志位避免同一目录下反复刷新 Neovim 的 LSP 配置这个 Flash 的代价很高。最终效果是临时文件按默认配置处理一旦切回项目文件配置能准确跳回对应模式且不会出现无意义的重复加载。7. 关于 context-mode 的几个延伸想法整套方案基本稳定之后我发现自己对上下文管理的理解发生了点变化。以前总觉得效率工具的价值在于快现在觉得更关键的是少操心。context-mode 让我不需要每次开工前回忆一堆琐碎细节把有限的注意力留在真正需要思考的代码逻辑上。扩展方向上我觉得有几个特别值得继续做一是把.ctxrc的解析逻辑做成一个统一的命令行工具而不是散落在 bash、lua 脚本里。比如ctx init、ctx run dev、ctx edit这类子命令能进一步降低使用和分享的门槛。二是在团队层面推广让.ctx/context.md成为项目的标配文档新成员 clone 下来之后AI 辅助问问题都能直接带着团队上下文而不是靠老员工口述半小时。三是我还在尝试把 context-mode 跟番茄钟或者时间块结合起来按任务类型而不是项目目录来定义模式——比如写作模式深度编码模式代码审查模式各自的窗口布局、通知策略、AI 提示词都不同。最后想分享一个小技巧无论你准备用 tmux、Neovim 还是 AI 工具来实现 context-mode一定要从最小可用版本开始不要一上来就配十几个脚本。先解决最痛的那个场景比如只做 tmux 会话恢复用顺手了再一层层加工具层、协作层。我最初就是急着一口气把所有模式都配上结果踩了坑再逐步精简绕了不少远路。这套东西的内核是把记住上下文这件事从你的大脑转移到文件系统和工具链里而这个转移过程本身也是需要迭代优化的别指望一次到位。