StepFold · 步骤折叠
DSH(DeepSeek Harness)Web UI 的界面插件,干两件事:
- 步骤折叠:把一轮回答里的过程步骤默认收成最近 5 步,点一下展开全部;
- 协作挡位:在输入框上方加一排开关——只回答 / 分步骤 / 无限制,决定这一轮助手能做到哪一步。
当前版本 1.2.2,更新记录见 CHANGELOG.md。
一、协作挡位
AI 接到一句话就埋头干活,中途看不到它想干什么,跑偏了只能等它跑完再回头。挡位把「这一轮能做到哪一步」变成你按得动的一个开关:
| 挡位 | 助手能做什么 | 什么时候用 |
|---|
| 只回答 | 只能读文件、搜内容、看网页;写文件、改文件、跑命令、派子代理一律被宿主拦下来 | 想先把事情问清楚、听听方案,不想它动手 |
| 分步骤 | 动手前先报 3–6 条计划(每条一句话,带文件名),然后一路做完;有匹配的技能先加载、顺手发现的问题最多提一条先问你、计划变了先说「调整:…」;不可撤销的动作先停下问你 | 想看着它干、但不想一步步批准 |
| 无限制 | 和平时一样(默认) | 简单活,不想管 |
要点:
- 挡位按会话各算各的:这个会话设成「只回答」,另开会话仍是默认。
- 开关状态跟着会话日志走:刷新页面、重开会话、从旧会话 fork 出来,挡位都还在。
- 随时可以打断:分步骤挡位下,你在它执行期间发的消息会在下一个步骤边界被收到,不用等它整轮跑完。
- 计划外的事按可逆性处理:干到一半发现必须做的事没在计划里——能撤销的(多改一个文件、多跑一次测试、下载到临时目录)它会先说一句再接着做;不能撤销的(装全局包、改系统配置、删目录、强制推送、花钱)会先停下来说明原因,等你点头。
- 分步骤会先找技能、也会问优化:任务如果正好是某个技能的活(代码审查、诊断 bug、架构选型、测试先行等),它会先加载那个技能、照技能的要求做;没有明显匹配的就不硬套。读到「该优化但不在这次需求里」的地方,最多提一条问你,你点头它才动——范围只限这次改动附近的文件,不会为这个把整个项目重扫一遍。
- 「只回答」是硬拦,不是提示词里求它别动手:被拦时它会说「当前挡位拦住我了」,再把该讲的讲清楚。
挡位开关也可以用命令切:
/stepfold answer # 只回答(也认 1、only、只回答)
/stepfold steps # 分步骤(也认 2、分步骤)
/stepfold free # 无限制(也认 3、无限制)
二、步骤折叠
装完之后,聊天里那种「思考 / 读文件 / 跑命令」排成一长串的步骤组,每组底部会多一条横条:
显示全部 11 步(还有 6 步) ←—— 收起时(默认)
只看最近 5 步 ←—— 展开后
一件事干得久一点,过程步骤就会铺满半屏,真正的回答被顶到下面。你想盯的是「它现在在干嘛」和「它最后说了什么」,不是前面那十行。所以默认只留最近 5 步。
行为细节:
- 所有轮次都折,包括正在运行中的那一轮,最新几步一直在。
- 横条只在这一组超过 5 步时出现,4 步的组不出现,不占地方。
- 每组独立展开:你展开了第 3 轮,不影响第 7 轮。
- 步骤变了会自动校准:宿主重新渲染、切换会话、步骤增减,横条都会跟着更新;组里不超过 5 步时横条自己消失。
- 折叠不改变宿主自己的折叠按钮:一组整体收起时,宿主的那个箭头照样能用。
安装
在 DSH 里用插件管理器安装这个目录:
plugin_manager install_bundle,target = F:\DSH产出\StepFold
安装后:
- 挡位功能需要重启 DSH——它新增了宿主半边(注册投影、命令、提示词段、工具守卫),宿主的模块缓存只在启动时求值一次。
- 重启后刷新页面(Ctrl+Shift+R)。只改
client.js 的话刷新页面就够,不用重启。
- 如果没生效,检查插件列表里
@local/step-fold 是否启用。
卸载
插件管理器里禁用或删除 @local/step-fold。卸载时会:
- 清掉插入的横条、样式和
<html> 上的标记类,聊天界面恢复原状;
- 撤掉挡位相关的投影、命令、提示词段和工具守卫——提示词和没装插件时一字不差。
注意:历史会话里留下的 /stepfold 命令记录会一并失效,那些会话的挡位回到「无限制」。
技术说明
client.js(浏览器端,纯界面)
- 步骤折叠:不替换宿主组件,只注入一条 CSS(
:nth-last-child 藏起较老的行)+ 一个 MutationObserver 补横条。依赖宿主的 data-step-process / data-step-process-body / data-step-process-content 三个属性,宿主改版最坏情况是折叠失效,不会弄坏聊天本身。
- 挡位开关:一个真正的 React 组件,注册在
conversation.input.dock 槽位(输入框上方那排)。状态用宿主的 useProjection('stepfold') 读,点击走 ctx.remote.commands.execute(sessionId, '/stepfold xxx', [])。
index.js(宿主端)
- 投影
stepfold:从会话日志折叠出挡位。只认 command/run(/stepfold xxx)和 command/done。不写自定义事件——插件的 live append 设不了 ignorable 标记,写进去的会话下次可能打不开。
- 提示词段
stepfold:collaboration-mode:按当前挡位注入一段规则,无限制挡位返回空串(等于不注入)。
- 工具守卫
ctx.tools.guard():只回答挡位拦写/跑/派类工具。用 guard 而不是 tools/pre-execute,因为 guard 是单调同步守卫,排在后面的插件没法把拒绝翻回允许。
- 命令
/stepfold:界面按钮和手敲命令走同一条路,不维护两套逻辑。
开发
node --check index.js # 语法
node --check client.js # 语法
node test/helpers.test.mjs # 客户端纯函数单测(21 条)
node test/host.test.mjs # 宿主半边单测(28 条):认挡位 / 折叠日志 / 拦工具 / 提示词
node test/smoke-client.mjs # 假 DOM 烟测:折叠 / 展开 / 撤按钮 / 挡位开关 / 卸载
node test/runtime-probe.mjs # 真机自检(可选,需要本机有 dsh 运行时)
test/smoke-client.mjs 用最小 DOM + 最小 React 桩把 client.js 跑一遍,验证的是「插件加载不会炸」和判定逻辑,不是视觉效果——视觉效果只能在浏览器里看。
test/runtime-probe.mjs 更硬一层:把真实的 dsh 服务(systemPrompt / sessionProjections / tools / commands)装进一个空 cordis 上下文,再挂上本插件,确认提示词段、命令、守卫真的进了宿主的登记表——这类「API 用错了」的错误单测看不出来,只会等你重启 DSH 时炸。
已知限制
- 只对步骤组生效,不折「思考」正文(那是另一类节点,宿主自己有折叠)。
- 5 步这个数字是写死的常量(
KEEP_STEPS),没做成设置项。
- 挡位不会自动回落:决策时讨论过「一轮做完自动跳回分步骤」「发『开工』自动切无限制」,这一版没做(它需要往会话日志写状态,得先确认宿主的 append 契约)。现在要换挡就点一下,或敲命令。
- 挡位的拦截是按工具名判断的(见
index.js 的 BLOCKED_TOOLS)。宿主以后新增的动手类工具默认放行,需要的话把它加进那张表。
许可
MIT