runcat-tommy/dsh-windows-c-cleanup ↗★ 0
dsh-windows-c-cleanup
提供Windows系统盘扫描、分级规划与清理迁移工具 适合需要AI辅助分析Windows C盘空间占用并安全执行清理迁移的用户
安裝
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:runcat-tommy/dsh-windows-c-cleanup說明文件
閱讀完整 README ↗使用
插件注册一个工具 disk_cleanup:
| 参数 | 取值 | 说明 |
|---|---|---|
action | scan | plan | apply | migrate | rollback | trash | 必填。scan/plan 只读;apply/trash 执行清理(M2);migrate/rollback 迁移与回滚(M3) |
scope | hotspots | full | hotspots 只按规则库测热点(快);full 追加全盘 Top-N 大目录(默认) |
reportPath | 路径 | 报告落盘位置,缺省 工作目录/C盘清理报告-.md |
format | markdown | json | both | 报告格式,缺省取配置 defaultReportFormat;json 产出机器可读报告(传 x.md 时会同时写同名 x.json) |
items | 路径数组 | 要清理的具体路径(谨慎层必填:只接受用户逐项确认过的路径) |
grade | safe | caution | migrate | 按层级选范围:safe 可批量;caution 必须同时给出 items;migrate 配合 action=migrate 自动挑选迁移层 |
mode | permanent | trash | 删除模式:trash 移到其他盘暂存区(可恢复,默认);permanent 永久删除 |
trashPath | 路径 | 暂存区位置,必须位于其他盘(同盘移动不释放空间);缺省 :\to_delete |
dryRun | 布尔 | 默认 true:只列出将要执行的动作,不删任何文件;用户确认后才传 false |
elevation | none | dism | cleanmgr | dism+cleanmgr | 是否一并触发管理员级系统清理(会弹 UAC) |
targetDrive | 如 D: | 迁移目标盘;缺省自动选空闲最大的非系统盘 |
extraRulesFile | 路径 | 本次扫描叠加的用户规则文件 |
调用示例(自然语言即可,模型会映射到工具):
帮我扫一下 C 盘,看看哪里占地方最大
工具会产出:
- 工具返回值:盘符、剩余空间、各层可释放字节数、大头 Top-N、迁移目标盘建议;
- 可视化报告(Markdown):汇总 → 🟥 大头 → 🟢/🟡/🟠/🔴/🔵 五级清单 → 执行结果。
执行清理(M2)
执行是两阶段的,安全默认值不靠调用方自觉:
- 预演:
apply不传dryRun时默认dryRun: true,只输出「将要删什么、多大、为什么」的逐项清单,并落盘一份C盘清理执行报告-.md; - 执行:用户确认后,才用
dryRun: false真正执行。
// 1) 安全层批量预演(不删文件)
{ "action": "apply", "grade": "safe" }
// 2) 用户逐项确认后的谨慎层(必须列出具体路径)
{ "action": "apply", "items": ["C:\\Users\\\\AppData\\Local\\Temp\\某缓存"], "mode": "trash", "dryRun": false }
// 3) 永久删除(需用户明确同意)
{ "action": "apply", "items": ["..."], "mode": "permanent", "dryRun": false }
// 4) 附带管理员级系统清理(会弹 UAC,用户拒绝则如实回报)
{ "action": "apply", "grade": "safe", "dryRun": false, "elevation": "dism+cleanmgr" }
执行报告与返回值会给出:逐项结果(已删除 / 已入暂存区 / 部分删除 / 已拒绝 / 需提权)、逐项测量合计释放量、盘符空闲净增、被拒绝项的完整理由、提权脚本路径与日志摘要。
迁移与回滚(M3)
「删掉」只是治标——企业微信、WPS、浏览器、包管理器的缓存删完会再长回来(实测一轮清理后约 11 GB 被应用自己重建)。迁移是治本:把目录搬到其他盘,在原位置留一个目录联接(junction),应用完全无感。
// 1) 预演迁移(不动数据)
{ "action": "migrate", "items": ["C:\\Users\\\\AppData\\Local\\npm-cache"], "targetDrive": "D" }
// 2) 用户确认后执行;原目录变成 junction,数据在 D:\dsh-cc-migrated\npm-cache
{ "action": "migrate", "items": ["..."], "targetDrive": "D", "dryRun": false }
// 3) 自动挑选规则库里的 🟠 迁移层
{ "action": "migrate", "grade": "migrate", "dryRun": false }
// 4) 后悔了:依据台账搬回并删除联接
{ "action": "rollback", "dryRun": false }
不可让步的执行顺序:复制 → 校验(大小与文件数)→ 删除源 → 建立联接 → 校验联接可读。任何一步失败都会清理副本并保持原状,绝不留下「半迁移」状态让用户自己收拾。具体保证:
| 情况 | 行为 |
|---|---|
| 目标与源在同一盘 | 拒绝(移动不释放空间) |
| 目标同名目录已存在 | 拒绝并提示,绝不合并 |
| 目标盘空间不足 | 拒绝,并给出需要的空间 |
| 源目录被应用占用、删不掉 | 回滚已复制的副本,原状态不变,提示先关闭应用 |
| 复制成功但建联接失败 | 明确报告数据已在新位置、老路径不可用,绝不谎报成功 |
| 回滚时源位置不是联接或指向不一致 | 拒绝回滚,避免覆盖用户后来放回的数据 |
| 迁移台账 | \ledger.jsonl,逐条记录源/目标/大小/时间/方法 |
app-config 类规则(如 npm 缓存)除搬数据外,还会返回建议命令(例如 npm config set cache "D:\..."),但不自动修改应用配置。目录联接在 Windows 上不需要管理员权限。
历史趋势与 JSON 报告(M4)
删掉的缓存会长回来——本机实测一轮清理后约 11 GB 被应用自己重建(企业微信升级 1.93 GB、%TEMP% 1.47 GB、WPS 插件 1.97 GB、Chrome ~0.8 GB、uv 327 MB)。所以每次扫描都会往 \windows-c-cleanup\history.jsonl 追加一条记录,并与上一次对比:
📈 与上一次扫描(24 小时前):剩余空间 −1.20 GB | 长回来 3 项 | 被释放 1 项 | 增长最多:…\WXWork\upgrade +1.93 GB
Markdown 报告里渲染成「📈 历史趋势」区块(长回来的 / 被释放的 / 新出现的 / 本次未再测到的);机器可读版本用 format:
{ "action": "scan", "format": "both" } // → C盘清理报告-.md + 同名 .json
JSON 报告带 schema: "dsh-windows-c-cleanup/report@1" 版本号,含五级分组、大头、趋势与告警,可直接喂给 GUI / 脚本 / 监控。趋势只在路径交集上比较:扫描被时间预算截断时,「上次有、这次没有」不等于「已被清理」,报告里用 previousPartial 标注。
定时扫描与告警(M4)
默认关闭——后台扫盘会占用你的磁盘 I/O,属于需要你点头的行为。打开后:
- id: windows-c-cleanup
name: dsh-windows-c-cleanup
config:
schedule:
enabled: true
intervalHours: 24
alertFreePercent: 10
scope: hotspots
- 低于
alertFreePercent时写一条告警记录(下次扫描的工具输出 / 报告顶部会显示),并经ctx.logger输出warn日志; - 宿主只提供
ctx.logger/ctx.effect,没有定时器服务,所以用 Node 定时器 +unref()+ctx.effect托管释放; - 四道保护:单飞(上一轮没跑完就跳过本轮)、首次延迟 1 分钟(避开启动抢 I/O)、整轮 try/catch(失败只记日志)、
unref()(不阻止宿主退出)。
清理面板(M5)
插件在 Web GUI 里注册一个对话视图 tab(conversation.view,additive list 插槽,不覆盖任何现有界面):打开任意会话,切到「磁盘清理」就能完成「看懂 → 勾选 → 预演 → 执行」。
实拍(中文界面,与本文档语言一致;英文界面见 README.en.md):
| 中文界面 |
|---|
![]() |
面板是薄的:它不做任何业务判断,分级、安全闸、测量、释放量核算全部复用宿主侧既有模块;面板调用的「预演」和「真执行」走的是同一个 executeCleanup(只有 dryRun 不同),所以预演里出现的每一项、每个理由都与真执行一致。
功能区对照表(每个区块左上角都有名字标签)
面板从上到下分成这些区块,每块左上角都有一个固定名字的小标签(中英各一套,测试 10.1–10.6 守着)。要指哪一块,直接说名字就行:
| 模块名 | 位置 | 里面有什么 |
|---|---|---|
| 概览 | 最上方标题栏 | 盘符剩余/总量与已用比例;右侧两个状态片:迁移目标(盘符 + 可用空间)、定时扫描开关 |
| 操作区(扫描 → 选择 → 预演) | 标题栏下方 | 三组相邻控件,组间有向右箭头:① 范围 + 扫描 C 盘 ② 已选 N 项 + 清空选择 ③ 删除方式 + 预演 + ?。这里没有「确认执行」——执行入口只有一处,在「预演结果」里 |
| 预演结果 | 紧跟在操作区下方(点「预演」后出现) | 每项会发生什么(移到暂存区 / 永久删除 / 需提权 / 被拒)、合计释放量、被拒清单;面板唯一的「确认执行」按钮就在这里,旁边还有「再预演一次」。标题右侧的动态文字才是"这次预演算出了什么" |
| 状态与提示 | 预演结果下方 | 进行中的操作(⏳)、上一次操作的结果提示、报错,以及扫描被时间预算截断时的告警 |
| 清理候选(🟢/🟡/🟠/🔴) | 中部四列卡片 | 可安全删除 / 谨慎删除 / 可迁移 / 保护名单,逐项路径、大小、判定理由;保护层不可勾选 |
| 长期防护 | 候选下方(可折叠) | 改设置或使用习惯就能反复受益的 6 条长期措施 |
| 执行进度 | 点「确认执行」后出现(候选下方) | 任务号与状态、进度条(来自宿主逐项记账)、逐项明细、释放量、取消任务 |
| 迁移预览 | 点卡片里的「迁移预览」后出现 | 源 → 目标映射、文件数、目标盘是否够、需要你手动改的应用配置;底部是迁移按钮 |
| 记录与产物 | 面板最底部 | 历史台账与报告目录的绝对路径(报告落到磁盘、保持中文) |
有两点值得注意:
- 「预演结果」是唯一被上移到操作区正下方的动态区块(其余动态区块仍在候选卡片下方)。这样点完「预演」,结果和它的执行按钮就在你刚点的那排按钮下面,不用翻过四列候选卡片;没有预演结果时,这个位置留给「状态与提示」,扫描完的提示同样紧贴操作区。
- 执行入口只有一处:「确认执行」只存在于「预演结果」里。工具栏不再提供执行按钮,于是"没预演就执行"在界面上根本没有入口(宿主侧的
guardTargets与dryRun契约不变,仍是最终防线)。预演后改动勾选或删除方式会让预演作废、整块结果消失、需要重新预演;永久删除未勾确认框时按钮不可点,原因写在按钮旁边(测试 8.1–8.9)。 - 按用户要求,原「历史对比」模块(与上次扫描的时间差、长回来的目录)已从界面移除;宿主的
state.trend字段仍照常返回,需要恢复时接回来即可。
工具栏按「同组相邻」排成三组,组间各有一个向右箭头标明先后关系,从左到右:
| 组 | 控件 | 说明 |
|---|---|---|
| ① 扫描 | 范围 下拉 + 扫描 C 盘 | 选 hotspots/full,然后开扫(已扫过则显示「重新扫描」)→ |
| ② 选择 | 已选 N 项 + 清空选择 | 实时计数与一键清空 → |
| ③ 预演 | 删除方式 下拉 + 预演 + ? | 「预演」右边就是问号说明按钮;执行按钮不在这里(见「预演结果」) |
箭头是纯装饰(aria-hidden,读屏会跳过),左右各留 12px 再加组内 6px 间距,所以留白明显,读起来就是「先扫描 → 再选择 → 最后预演」。
可辨识度(专门调过,不是默认样式):
-
「扫描 C 盘」「清空选择」「预演」三个动作按钮在可用时是品牌色加粗描边 + 底色淡染 + 投影 + 加粗字,一眼就能看出是按钮;不可用时保持原来的浅底灰边半透明样式——不能点的按钮绝不能画得像能点;
-
「确认执行」(在「预演结果」里)是实心品牌色:实心=最后一步动作,描边=可点的普通动作,层级不混;
-
「预演」右边的
?是 24px 圆形按钮(2px 品牌色描边、底色淡染、加粗问号、悬停放大)。点开就地说明,文案刻意写得直白:第一句就是「点「预演」= 先干跑一遍,什么都不删」,再列三条要点(真的检查能不能删 / 真的算权限与空间 / 每项写明会发生什么),最后才提两点注意(数字是估算、查不出文件被占用)。 -
五级卡片:🟢安全 / 🟡谨慎 / 🟠可迁移 / 🔴保护,逐项显示路径、大小、判定理由;保护层不可勾选;
-
两步执行:执行入口只有一个,在「预演结果」里,所以必须先「预演」才可能执行;勾选或模式一变,预演即作废、结果整块消失,得重新预演。唯一还能拦住执行按钮的是"永久删除未勾确认框",此时按钮不可点,原因直接写在按钮旁边(禁用按钮的 tooltip 在多数浏览器里根本弹不出来,只写 tooltip 等于没写)——由
runGate()纯函数判定,测试 8.1–8.9 钉住; -
真实进度:进度条来自宿主的逐项记账回调(不是猜日志文本),随时可「取消任务」;
-
迁移预览:先看「源 → 目标」映射、文件数、目标盘是否够,再决定是否迁移;需要你改的应用配置只提示、不代改。
切到对话页再切回来,正在做的事不会丢
面板是插在会话视图里的,切走会卸载组件(本地 React 状态随之清空)。所以凡是"正在发生的事",面板都以宿主为准去认领,而不是自己记着:
| 你切走时 | 宿主的权威状态 | 切回来看到 |
|---|---|---|
| 正在「扫描 C 盘」 | panelState().scan = {running, scope, startedAt}(扫描本来就跑在宿主侧,组件卸载不影响它) | 「正在扫描 C 盘」的 ⏳ 立刻回来,之后每 1.2 秒问一次宿主 |
| 扫描刚在你切走时跑完 | 结果在宿主缓存里(scan-view) | 候选卡片直接出现,并补一条「扫描完成」提示 —— 不用重新点一次扫描 |
| 正在真清理 / 迁移(有任务在跑) | panelState().runningJobIds | 拿 jobId 接上进度轮询,「执行进度」的进度条与逐项明细继续走 |
宿主同一时刻只扫一次:面板回来后你要是又点了「扫描」,会接上还在跑的那一次(省一次全盘遍历,也不会让两份结果互相覆盖;测试 12.3–12.5)。
有一点如实说明:勾选与预演结果不跨卸载保留(改动勾选或删除方式本来就会让预演作废,回到面板时按"重新勾选 → 重新预演"走)。scan 与 runningJobIds 这两个字段是 0.5.1 加的,老宿主(还没重启 dsh web)缺字段时面板退化成原样行为、不报错(测试 11.6)。
中英双语
面板与宿主文案都是中英两套,跟随 DSH 的语言设置自动切换,不需要手动选语言:
- 界面文案由客户端字典提供(
client/src/i18n.ts,中英各 120 条,键集合严格一致),tab 标题是函数式标签,语言一换标题即跟着换,无需重新注册; - 面板每次调用宿主 RPC 都会带上当前
locale,所以宿主生成的文案也跟着切换:规则库的每一条判定理由、安全闸的 12 条拒绝理由、执行器的逐项计划动作、迁移配置提示、调度状态。英文文案放在src/rules/default-rules.en.json,按规则 id 与中文严格对齐(101 条规则 + 6 条长期防护,有测试守着覆盖率和「英文里不得出现中文」); - 切语言不会让你重扫一遍(
scan-view端点):扫描结果里的规则说明、截断告警都是宿主生成那一刻渲染好的字符串,切语言本来不会变——所以宿主额外提供「用缓存里那次扫描按目标语言重新出视图」(不碰盘、不测量,实测 0ms),面板在语言变化时自动调它,并顺带重跑一次预演与迁移预览,让界面不再中英混杂(实测截图暴露过这个问题); - 提示句存的是「键 + 参数」(
Notice,见client/src/panel.tsx):渲染时才取当前语言的t,所以「扫描完成…」「预演完成…」这类提示会跟着语言切换,而不是冻结在生成时的语言(类型上也堵住了:传字符串编译不过); - 默认中文:模型工具(
disk_cleanup)那条路不传locale,输出与历史完全一致;只有 Web 面板会传en; - 英文缺项一律回退中文原文,宁可显示中文也不显示空洞;
- 接线有时序要求:字典必须在框架渲染带
locale:的注册项之前登记好,所以客户端插件用ctx.inject(['locale'])等语言服务就绪再接线;相应地dsh.client.inject里也声明了@deepseek-ai/dsh-client-locale(这属于包元数据变更 → 首次升级要重启dsh web,之后改文案只刷新页面即可)。
浏览器与宿主之间走 Connection 的通用 RPC 通道 /dsh-c-cleanup(authority: loopback,只接受本机调用),端点包括 state / scan / scan-view / preview / execute / migrate / progress / cancel / history 等。任务表是宿主内存态,宿主重启即清空。
state 端点是面板的"重新挂载入口",除了盘符与调度状态,还如实报告此刻在不在扫盘(scan)与有哪些任务在跑(runningJobIds)——面板切走再回来靠这两个字段把界面接上(见上一节),扫描本身与任务本身都跑在宿主侧,不受组件卸载影响。
落地条件(平台机制决定,不是本插件的选择):
| 改动 | 需要做什么 |
|---|---|
新增/删除插件包、改 dsh.client 字段 | 重启 dsh web(包元数据判定被宿主永久缓存) |
只改 client/client.js 内容 | 刷新页面即可(bundle 带 no-cache;本 profile 的 HMR 是关闭的) |
配置
在 profile 的 cordis.patch.yml 中覆盖(patch 会整体替换该行 config,不做深合并):
- id: windows-c-cleanup
name: 'dsh-windows-c-cleanup'
config:
reportDir: 'D:\reports'
defaultScope: full
hotspotTimeBudgetMs: 70000
topTreeTimeBudgetMs: 70000
topTreeMaxDepth: 3
bigItemThresholdBytes: 2147483648
allowProtectedOverride: false
allowExplicitUnmatched: false
defaultDeleteMode: trash
trashPath: 'D:\to_delete'
migrationRoot: 'D:\dsh-cc-migrated'
historyPath: 'C:\Users\\.dsh\windows-c-cleanup\history.jsonl'
defaultReportFormat: markdown
schedule:
enabled: true # 默认 false:不主动占用你的磁盘 I/O
intervalHours: 24
alertFreePercent: 10
initialDelayMinutes: 1
scope: hotspots
extraRulesFile: 'D:\my-rules.json'
| 配置项 | 默认 | 说明 |
|---|---|---|
reportDir | 当前工作目录 | 报告输出目录 |
defaultScope | full | 默认扫描范围 |
hotspotTimeBudgetMs | 70000 | 热点清单时间预算(超时截断并标记) |
topTreeTimeBudgetMs | 70000 | 全盘 Top-N 时间预算(两个扫描并行,总时长约等于较大者) |
topTreeMaxDepth | 3 | Top-N 遍历深度 |
bigItemThresholdBytes | 2 GiB | 「大头」判定阈值 |
allowProtectedOverride | false | 是否允许用户规则覆盖保护名单(默认禁止;硬约束项永不放行) |
allowExplicitUnmatched | false | 是否允许清理未收录规则库的显式路径(默认「不明即不删」) |
defaultDeleteMode | trash | 默认删除模式:trash 移到其他盘暂存区,permanent 直接删除 |
trashPath | :\to_delete | 暂存区位置(必须与其他盘同盘不同卷才释放空间) |
migrationRoot | :\dsh-cc-migrated | 迁移根目录;迁移台账 ledger.jsonl 与之同目录 |
historyPath | \windows-c-cleanup\history.jsonl | 扫描历史(趋势对比数据源);故意放在不会被清理的位置 |
defaultReportFormat | markdown | 默认报告格式:markdown / json / both |
schedule.enabled | false | 是否启用定时扫描(默认关闭,需你显式同意) |
schedule.intervalHours | 24 | 定时扫描间隔(小时) |
schedule.alertFreePercent | 10 | 剩余空间占比低于该值时写告警 |
schedule.initialDelayMinutes | 1 | 首次执行延迟,避开宿主启动抢 I/O |
schedule.scope | hotspots | 定时扫描范围(比 full 快且省 I/O) |
extraRulesFile | 无 | 用户附加规则文件 |
