工作区失效记录:自动检测 + 清理
核心原则:agent 自己查、自己清。用户只需要回一句"是"或"否",不用懂原理、不用执行命令、不用碰配置文件。
一、什么时候检测(满足任一即可)
- 用户在本会话中删除 / 重命名 / 移动了任何工作区目录 ——
在动作完成的那一刻立刻静默检测并提示,不需要用户再说任何话。这是最主要、最自动的一条。
(若用户删的是工作区目录,几乎必然留下失效记录。) - 用户提到任何与工作区相关的话题,包括极简口令:
清理工作区、工作区清理、工作区记录、整理工作区。
用户只打"工作区"两个字也算。 - 出现工作区打开或切换失败、提示"路径不存在""目录不存在"的迹象。
- 用户抱怨"最近打开的工作区列表很乱""列表里有个已经不存在的"。
约束:
- 同一会话只检测一次,不要反复查
- 检测结果没有失效记录 → 什么都不要说,直接继续用户原本的任务
- 用户说过"不用管 / 我知道了 / 别管这个" → 本会话不再提
二、静默检测(不解释、不输出过程)
先定位配置文件。Windows:
Get-ChildItem -LiteralPath (Join-Path $env:USERPROFILE '.workbuddy\storage') `
-Recurse -Filter 'workspace-picker.json' -File -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty FullName
macOS / Linux(等价命令,路径为 ~/.workbuddy/storage):
find "$HOME/.workbuddy/storage" -name workspace-picker.json -type f 2>/dev/null
对每个结果:以 UTF-8 读取 → 遍历 explicit-workspaces → 对每项校验 path 指向的目录是否存在 → 统计失效条数。
判定规则:path 指向的目录不存在即为失效。
⚠️ 解析失败必须跳过,不得写回:若某个 workspace-picker.json 不是合法 JSON(空文件、被截断、被别的程序写坏),
不要修复、不要重建、不要覆盖——跳过它,并在提示里附一句"另有 1 个配置文件无法解析,已跳过"。写坏别人的配置远比漏清一条严重。
⚠️ storage 目录不存在(全新安装、从没删过工作区)→ 视为"没有失效记录",直接结束,什么都不要说。
不要报错、不要提示"未找到配置文件"。
第二个数据源:SQLite(新版本必查,漏了就清不干净)
新版本 WorkBuddy 把"最近打开的工作区"搬进了 SQLite 主库 ~/.workbuddy/workbuddy.db 的 workspaces 表(列:path、last_opened_at)。
UI 列表 = workspace-picker.json 的显式项 + workspaces 表的历史项,只清 JSON → 用户看到的列表里仍会残留旧条目。
只读查询(跨平台,用 Python 3 标准库;Windows 可用托管的 python,路径见环境):
import sqlite3, os
src = os.path.expanduser('~/.workbuddy/workbuddy.db')
con = sqlite3.connect(f"file:{src}?mode=ro", uri=True)
for p, t in con.execute("select path, last_opened_at from workspaces order by last_opened_at desc"):
print("OK " if os.path.isdir(p) else "GONE", "|", p)
- 库文件不存在 / 无
workspaces表(老版本)→ 跳过,不要报错 - 只读打开失败(被占用)→ 跳过并附一句"另有 N 条数据库记录未能核对"
顺带查一处:~/.workbuddy/storage/device/scoped/*/newTaskWorkspace.json 里的 cwd / lastLocalWorkspace
可能指向已删目录(决定"新任务默认落在哪个工作区"),失效时一并报告。
三、提示话术(照抄结构,把 N 和路径换成实际值)
你的「选择工作区」列表里有 N 条记录指向的工作区已经被删掉了:
- <路径 1>
- <路径 2>
要帮你清掉这 N 条无效记录吗?(是 / 否)
- 不要贴原理、不要贴 JSON、不要贴命令、不要贴命令输出
- 路径最多列 5 条,更多的用"等 N 条"收尾,别刷屏
- 用户答"否" → 回一句"好的,不处理",结束话题
- 用户未回答或直接换话题 → 视为否,不要重复追问
四、用户答"是"时执行
- 直接写回,不留备份:不生成
.bak、不复制副本、不留"回收站兜底"。清理类操作不留备份是硬性规则,也不要问"要不要备份"。 - 手工拼字符串重建 JSON,只保留
path仍存在的项(原因见"必守规则") - 以无 BOM 的 UTF-8 写回原文件
- 顺手清掉历史遗留备份:目录里如残留旧的
<原文件名>.bak-*,一并删除(只匹配同名前缀,避免误删同目录下其它程序的备份)。删除被沙箱拦截时跳过即可,不要中断或刷屏报错。 - 多份配置时逐份独立处理:互不影响
- 清 SQLite 里的
workspaces表:删除path目录不存在的行(先select出全部行、逐条os.path.isdir判定,再按path精确delete,最后commit())。库不存在或无该表则跳过。 - 修
newTaskWorkspace.json:若cwd/lastLocalWorkspace指向已删目录,改成当前工作区目录(仅改这两个字段,保持原 JSON 结构)。 - 一句话回报:
已清理 2 条,剩 1 条。重启 WorkBuddy 后列表就会刷新。(数字换成实际值)
写文件失败(被占用 / 无权限)时不要重试多次,也不要静默:回一句 清理失败:<原因>。文件未改动。。
失败时绝不留半成品文件,原文件必须保持未改动状态。
特殊情况:如果所有条目都失效、清理后 explicit-workspaces 变成空数组 [],
回报时补一句:列表已清空,WorkBuddy 会回到未选择工作区的默认行为。(不要因此拒绝执行或自作主张保留一条)
不要输出 diff、不要贴新 JSON、不要解释 BOM 是什么。
五、必守规则(技术红线)
| 规则 | 原因 |
|---|---|
判定用 Test-Path -LiteralPath <path>,路径含空格/中文也必须带 -LiteralPath |
避免通配符误判 |
读取必须 [IO.File]::ReadAllText($p, [Text.Encoding]::UTF8) |
Get-Content -Raw / type 会按 ANSI(CP936) 解码,中文路径会显示成 鍘嗗彶璁板綍娓呴櫎 这类乱码,从而误判成"数据损坏" |
写回必须 New-Object System.Text.UTF8Encoding($false) |
不加 BOM。原文件有 BOM、重写后没有,是正常行为,不是损坏 |
手工拼 JSON 字符串,绝不用 ConvertTo-Json |
PowerShell 5.1 会把单元素数组展平成对象,前端 JSON.parse 后 explicit-workspaces.forEach 直接崩 |
空结果写成 "explicit-workspaces": [] |
保持字段存在,别整个删掉 |
字符串转义:\ → \\," → \" |
否则 JSON 非法 |
storage 下可能有多份 workspace-picker.json(多账号/多作用域) |
逐个处理,只清一份可能没效果 |
| 目录被临时改名,或位于未挂载的网络盘/加密盘 | 会被判为失效。提示时已列出条数与路径,用户有最终否决权 |
| JSON 解析失败(空文件 / 被截断 / 损坏)→ 跳过该文件,绝不写回 | 擅自"修复"别人损坏的配置,比漏清一条严重得多 |
不生成任何备份副本(.bak / 副本 / 压缩包),直接写回原文件 |
硬性规则:清理类操作不留备份。也不需要询问用户 |
历史遗留的 .bak-* 只按同名前缀匹配删除 |
裸通配会误删同目录下其它程序的备份 |
| 非 Windows 平台不要照抄 PowerShell 命令 | 第二节另给了 macOS / Linux 的等价命令 |
写 SQLite 必须按 path 精确匹配删除,不要 delete from workspaces 不带 where |
表里还有有效记录,全表清空会毁掉"最近打开" |
写 SQLite 时不建备份、不删库重建、不动其它表(sessions / automations 等) |
这是应用主库,动别的表会丢会话和定时任务 |
| 应用正在运行时写 SQLite 通常可行(WAL),但清完必须复查一次 | 应用内存里可能还有旧列表,会在退出时回写 |
参考写回片段($keep = 过滤后要保留的项集合,$target = 目标文件完整路径,两者需在调用前自行赋值):
$nl = [Environment]::NewLine
$sb = New-Object System.Text.StringBuilder
[void]$sb.Append('{').Append($nl).Append(' "explicit-workspaces": [')
for ($i = 0; $i -lt $keep.Count; $i++) {
$k = $keep[$i]
if ($i -gt 0) { [void]$sb.Append(',') }
$p = ([string]$k.path).Replace('\','\\').Replace('"','\"')
$l = ([string]$k.label).Replace('\','\\').Replace('"','\"')
[void]$sb.Append($nl).Append(' { "path": "').Append($p).Append('", "label": "').Append($l).Append('" }')
}
[void]$sb.Append($nl).Append(' ]').Append($nl).Append('}')
[IO.File]::WriteAllText($target, $sb.ToString(), (New-Object System.Text.UTF8Encoding($false)))
六、平台
Windows / macOS / Linux 均可——全流程只有文件读写,不需要脚本、可执行文件或管理员权限。
配置目录:Windows %USERPROFILE%\.workbuddy\storage,macOS / Linux ~/.workbuddy/storage。
第二节的命令按平台各给了一份;下面"参考写回片段"是 PowerShell,
非 Windows 平台改成该平台等价的 UTF-8 文本读写即可(要点相同:不加 BOM、手工拼 JSON、不转义遗漏)。
七、明确不做的事
- 不删除任何工作区目录,只改
workspace-picker.json、workbuddy.db的workspaces表、newTaskWorkspace.json这三处 - 不安装依赖、不改系统设置、不碰注册表、不动
.workbuddy下其它文件 - 用户没有回答"是"之前,绝不写入任何文件
- 不要擅自创建定时任务。若用户希望"以后自动发现",可以提议为他建一个定期检查的任务,得到同意再动手