# ============================================================================
# 过滤层规则表 —— 小锋语音伴侣
# ============================================================================
# 这个文件是给"使用者自己调脾气"用的：
#   · 改完保存即生效，**不用改代码、不用重启**（研判服务每 5 秒重读一次）。
#   · 值都是正则表达式；多个候选用 | 连接；不区分大小写。
#   · 以 # 开头的是注释；[节名] 下面是 key = value。
#
# 定档只有三档，判不过往下走，命中即定档：
#   must（必播，不可静音） / may（可静音，受节流） / drop（直接丢弃）
# 分级主轴：**这次动作有没有"改变结果"**。
#   write / 有副作用的命令 / 对外发送  -> 改变了结果 -> must
#   read / 搜索 / 列目录 / 查看状态     -> 没改变什么 -> may
#   TodoWrite / 心跳 / 纯计数行         -> 噪声       -> drop
#
# 保存时的编码必须是 UTF-8（本文件含中文）。改坏了不要紧：研判服务读不动时
# 会退回内置默认值并写日志，不会因为规则表出错就哑掉。
# ============================================================================


[模式]
# result = 只报"改变结果"的（拍板 / 失败 / 结果变更），过程音一律不念
# all    = 连状态类也报（读文件、搜索这类）
# 说明：真正生效的模式由热键 Ctrl+F8 写在 logs/_flow_mode.txt，这里只是兜底默认值。
default = result


[工具·必播]
# 这些工具一调用就"改变了结果"，属 must 档。注意用工具名的完整匹配片段。
must = Write|Edit|MultiEdit|NotebookEdit|write_file|create_file|apply_patch|str_replace|insert_content|NotebookWrite


[工具·可静音]
# 只是"看了看"，没改变什么，属 may 档。
may = Read|Glob|Grep|LS|list_dir|search|WebFetch|WebSearch|Task|Agent|BashOutput


[工具·丢弃]
# 平台内部动作 / 与用户无关的自维护动作，属 drop 档。
drop = TodoWrite|TaskCreate|TaskUpdate|TaskList|KillShell|SlashCommand|ExitPlanMode


[命令·必播特征]
# Bash 命令命中这些词 -> 认为有副作用 -> must。
# 这里只列"改磁盘 / 改远端 / 改状态"的动作。
must_cmd = git\s+(commit|push|merge|rebase|tag|reset|checkout|stash|cherry-pick)|npm\s+(install|ci|publish|run\s+build)|yarn\s+(install|add|build)|pnpm\s+(install|add|build)|pip\s+install|poetry\s+add|docker\s+(build|push|rm|run)|kubectl\s+(apply|delete)|curl\s+.*-o\s|wget\s|Invoke-WebRequest|rm\s+-|Remove-Item|del\s+/|mkdir|mdir|New-Item|mv\s|Move-Item|cp\s+-r|Copy-Item|chmod|chown|rename|ren\s|tar\s+-x|unzip|Set-Content|Out-File|Add-Content|>>?\s*\S+\s*$|tee\s|netsh|reg\s+add|sc\s+(start|stop|config)|Stop-Process|Start-Process|taskkill|schtasks|publish|deploy|install|build


[命令·可静音特征]
# 只读类命令 -> may。
may_cmd = ^\s*(ls|dir|cat|type|head|tail|grep|rg|find|findstr|which|where|whoami|ps|tasklist|echo|pwd|cd|python\s+-c|python\s+--version|node\s+-e|git\s+(status|log|diff|show|branch)|du|df|stat|wc|sort|uniq|Get-ChildItem|Select-String|Get-Content|Test-Path)\b


[失败特征]
# 命中即升为 must（失败会改变后续走向，用户必须知道，否则白等）。
# 注意：宁可少判"成功"，不可把失败当成功（降级不撒谎）。
fail = Traceback|Error:|error:|(?-i:ERROR)|Exception|失败|出错|报错|No such file|not\s+found|command not\s+found|Permission denied|Access is denied|拒绝访问|timed?\s*out|超时|Killed|segmentation fault|exit(ed)?\s+(with\s+)?(code\s+)?[1-9]|returncode['\"]?\s*[:=]\s*[1-9]|status['\"]?\s*[:=]\s*[1-9]|isError['\"]?\s*:\s*true|exitCode[\s:='\"]*[1-9]|tool_error_code[\s:='\"]*[1-9]|sandboxDenied[\s:='\"]*true|interrupted[\s:='\"]*true


[成功特征]
# 用于区分"确实成功"与"未知"：拿不到结果字段时按未知处理（只报动作，不报成功）。
ok = exit(ed)?\s+(with\s+)?(code\s+)?0|returncode[\s:='\"]*0|status[\s:='\"]*0|isError[\s:='\"]*false|\bok[\s:='\"]*(true|1)\b|\bsuccess[\s:='\"]*(true|1)\b|success|成功|完成|exitCode[\s:='\"]*0(?![0-9])|tool_error_code[\s:='\"]*0(?![0-9])


[噪声]
# 命中即 drop（纯计数行、心跳、缓存命中之类）。
noise = ^[\s\d+\-\.x×%·|,]+$|已消耗|消耗\s*\d|tokens?\s*(used|left)?|cached|cache\s+hit|heartbeat|心跳|\bmd5\b|sha256


[冷却]
# 同一条（同文本键 / 同一动作槽）在这个秒数内只念一次。0 表示关闭去重。
# 跨通道去重的"动作槽"另有分档冷却，见代码 _CLASS_COOL（读得久、写得短）。
sec = 6


[节流]
# 状态类（may 档）在 all 模式下也压一压：同一动作槽的类级冷却（秒）。
status_sec = 8
