| name | file-tidy |
| description | 文件系统整理师——结构感知归位,帮跑错地方的文件回到该去的子目录。支持干跑模式和自定义 YAML 辅助配置。独立使用或被 weaver-自我迭代 调用。 |
文件系统整理师 — File Tidy v2
不是"把所有 .py 塞进 scripts/",而是学习项目已有的目录结构,把跑错地方的文件送回该去的地方。像一位细心的室友——先看家里怎么摆的,再帮你把跑偏的东西归位。
触发词
整理文件 / 整理目录 / 文件归类 / tidy / cleanup
何时使用
- 项目根目录散落文件,明显属于某个已存在的子目录
- 想把临时文件、备份文件找出来
- weaver 全局整理时自动调用
核心原则
- 结构感知优先 — 先学习项目已有的目录结构,再判断归属
- 高置信度才动 — 不确定的就放着,"不确定"比"猜错了"好一万倍
- 不创造新目录 — 只归到已有目录,不新建
- 不拆散项目 — 同一主题的文件保持在一起
工作流程
第一步:确定目标目录和模式
- 用户指定目录 → 用指定目录;否则用当前工作目录
- 默认干跑模式。用户说"执行"/"确认"/"动手"才进入执行模式
- 检查目标目录下是否存在
.tidy-rules.yaml。存在则加载(辅助信息),不存在则纯靠结构感知
第二步:建地图 — 学习项目目录结构
使用 PowerShell 扫描目标目录下的直接子目录,为每个目录建立"指纹"。
构造 <safe_zone_pattern>:
- 如果存在
.tidy-rules.yaml 且含 safe_zones:将每个 zone 的 glob 转为 regex(* → .*,? → .,其余字符用 [regex]::Escape() 转义),用 | 拼接
- 如果没有
.tidy-rules.yaml 或 safe_zones 为空:使用默认安全模式 ^(\.|node_modules)
# 扫描一级子目录(排除安全区)
Get-ChildItem -Path <dir> -Directory -Depth 0 |
Where-Object { $_.Name -notmatch '<safe_zone_pattern>' } |
ForEach-Object {
$dir = $_.FullName
$files = Get-ChildItem -Path $dir -File -Depth 0 | Select-Object -First 20
[PSCustomObject]@{
Name = $_.Name
Path = $dir
SampleSize = @($files).Count # 采样数量(最多 20),不是目录总文件数。如需总文件数做比例计算,单独 Get-ChildItem 计数
Extensions = ($files | Group-Object Extension | Sort-Object Count -Desc | Select-Object -First 3).Name -join ','
Prefixes = ($files | ForEach-Object { ($_.BaseName -split '[-_]')[0] } | Group-Object | Sort-Object Count -Desc | Select-Object -First 3).Name -join ','
}
}
指纹字段含义:
- Name:目录名(提取中英文关键词作为语义标签)
- Extensions:目录内最常见的文件扩展名(如
.py,.csv,.md)
- Prefixes:目录内文件最常用的前缀/词根(如
movie,elm,weather)
同时读取 .tidy-rules.yaml(如果存在)获取:
directories:目录别名(帮 AI 理解目录含义)
safe_zones:绝对不碰的目录/文件模式(支持通配符 *)
keep_at_root:必须留在根目录的文件
junk:临时/垃圾文件清理规则
第三步:清理层 — 识别垃圾文件
用 .tidy-rules.yaml 的 junk 规则扫描目标目录,标记建议删除的文件。
注意:
- 清理层在归位层之前运行——垃圾文件先过滤掉,不参与归位判断
- 清理结果标记为"建议删除",永远不自动执行删除
- 无
junk 配置时跳过此层
第四步:归位层 — 对游离文件逐项打分
对目标目录下的所有直接文件(不含子目录内的文件),逐一计算它属于每个已有子目录的分数。
评分维度
维度一:文件名 → 目录名匹配(权重 40%)
- 文件名和目录名共享相同关键词(子串双向匹配):40 分
匹配粒度:去除分隔符(
-、_、.、/)后的独立词段。如 movie_train 拆为 [movie, train],outputs/movie/ 提取 [outputs, movie]。共享词段越多分越高:2+个共享词段 = 40 分,1 个 = 30 分,无共享 = 看别名
- 文件名包含目录名关键词或反之:30 分
(如
movie_train.py → 目录 outputs/movie/)
- 通过
.tidy-rules.yaml directories 别名匹配:25 分
(如文件名含"前端"关键词 → frontend-design/ 目录)
- 无关键词重合:0-10 分
维度二:同源文件聚类(权重 30%)
以下子项取最高分,不累加。即同时满足多个条件时,只取最高的一项得分。
- 目标目录内已有同前缀文件(前缀匹配):30 分
(如
movie_report.md → 目录已有 movie_download.py)
- 文件扩展名在目标目录占主导(≥50%):20 分
- 文件扩展名在目标目录常见(25-49%):10 分
- 无明显同源关系:0 分
维度三:代码引用关系(权重 20%,仅代码文件)
- 文件内容中 import/require/reference 指向了目标目录内的模块:20 分
- 目标目录内文件的 import 指向了这个文件:15 分
- 非代码文件(.md, .csv, .json, .yaml 等):跳过此维度,总分按 80 分制折算
维度四:语义兜底(权重 10%,仅当前三项总分 < 30 时触发)
仅对文本类文件(.py, .md, .txt, .js, .ts, .java, .json, .yaml, .xml, .csv, .html, .css 等)触发语义兜底。二进制文件(.pdf, .png, .zip, .exe, .dll 等)直接跳过此维度。
- 读取文件内容摘要(前 30 行)
- 与各目录名 + directories 别名做语义匹配
- 有明确语义关联:10 分;模糊关联:5 分;无关联:0 分
- 重要:如果超过 2 个文件需要语义兜底,直接停止此维度,标记这些文件为"无法判断"。熔断后第 3 个及之后的非代码文件直接按"无法判断"处理,归入"未归类"。
综合评分公式
- 代码文件:维度一(0-40) + 维度二(0-30) + 维度三(0-20) + 维度四(0-10) = 总分(0-100)
- 非代码文件:维度一(0-40) + 维度二(0-30) + 维度四(0-10),按 (总分 / 80) * 100 折算
安全过滤(打分后应用)
以下条件任一满足,则不加入移动建议:
- 文件在
keep_at_root 列表中
- 文件是隐藏文件(
. 开头)
- 文件名/路径匹配
safe_zones 模式
- 目标路径已存在同名文件(冲突)
多目录等分时的仲裁策略
同分平局:当文件对多个目标目录得分相同且均为最高时,选择文件数更少的目录(避免把零散文件塞进已经很大的目录)。如果文件数也相同,选目录名与文件名关键词匹配度更高的。
第五步:展示计划
按四档输出:
整理计划 — <目标目录绝对路径>
▌ 建议删除(N 项)
<文件> → [删除] <reason>
▌ 建议移动 — 高置信度(N 项)
<源文件路径> → <目标路径> (XX分)
▌ 建议移动 — 请确认(N 项)
<源文件路径> → <目标路径> (XX分 ⚠ <低分原因>)
▌ 未归类(N 项)
<文件> (<原因>)
已扫描 XX 个文件,建议删除 X 项,建议移动 X 项(其中 X 项需确认),未归类 X 项。
执行:整理文件 --confirm(执行全部建议移动 + 建议删除)
只看高置信度:整理文件 --safe(只执行高置信度移动 + 建议删除)
格式规则:
- 每个文件必须标注归属于哪个子目录
- "请确认"项必须说明低分原因(如"可能与 scripts/ 或 outputs/movie/ 均有关")
- 未归类项必须说明不动它的原因
- 建议删除不自动执行,需用户确认
- 如果所有文件都在合适的位置,输出"目录已整洁,无需整理"
置信度阈值
| 总分 | 动作 |
|---|
| ≥ 70 | 高置信度 — 直接建议移动 |
| 40-69 | 中置信度 — 建议移动,标注"请确认" |
| < 40 | 不处理,列入"未归类" |
第六步:用户确认
展示计划后询问:
"执行整理(全部移动+删除)/ 安全模式(只动高置信度项)/ 只看归类(只移动高置信度,不删)/ 取消?"
- 执行整理:执行所有建议移动 + 所有建议删除
- 安全模式:只执行高置信度移动 + 只执行建议删除,中置信度不动
- 只看归类:只执行高置信度移动,删除项不动
- 取消:不执行任何操作
第七步:执行
# 检查目标目录是否存在(不存在则跳过,绝不创建新目录)
if (-not (Test-Path "<target>")) {
Write-Output "ERROR 目标目录 <target> 不存在,跳过"
continue
}
# 移动文件(不覆盖已存在的同名文件)
if (-not (Test-Path "<target>/<filename>")) {
Move-Item -Path "<source>" -Destination "<target>/<filename>"
Write-Output "OK <source> → <target>/<filename>"
} else {
Write-Output "SKIP <source> → <target>/<filename> 已存在"
}
执行完成后输出摘要:
整理完成 — 移动 X 项,跳过 Y 项(冲突),删除 Z 项,未处理 W 项
被 weaver 调用时
weaver 在全局整理时调用 file-tidy:
- weaver 以干跑模式启动 file-tidy
- 结果嵌入 weaver 变更摘要的"文件系统整理"段落
- 用户在 weaver 摘要中确认后,weaver 再次调用 file-tidy 执行
命令选项
| 选项 | 行为 |
|---|
| (默认,无参数) | 干跑模式,只展示计划 |
--confirm | 执行全部建议移动 + 建议删除 |
--safe | 只执行高置信度移动 + 建议删除 |
--classify | 只移动高置信度,不执行删除(对应"只看归类"模式) |
--dry-run | 只出报告不动手 |
安全规则
- 绝不自动删除文件 — 删除标记需用户二次确认
- 绝不覆盖已有文件 — 目标已有同名文件时跳过并报告
- 绝不创建新目录 — 只使用项目已有的子目录
- 绝不碰隐藏文件 —
. 开头默认跳过
- 不碰安全区目录 — 根据 safe_zones 配置排除
- 不确定就放着 — 低置信度文件不动,不猜测
注意事项
- 本 skill 执行时,必须实际运行 PowerShell 命令扫描目录,不能凭空猜测文件位置
- 评分必须对每个文件、每个目标目录逐项计算,不能跳步
- 目录指纹中的"前缀"独立于扩展名——
movie_train.py 和 movie_report.md 共享前缀 movie_
.tidy-rules.yaml 是辅助而非驱动——没有它也应该能完成归位判断