mcpField
U
unslop

unslop

github.com/howells其他541 次安装今天更新
代码仓库

关于 unslop

unslop 是用于清理代码中冗余内容的 Agent 技能,面向编码 Agent。它提供 19 类代码模式和按差异范围执行的流程,并要求清理不改变程序行为。

主要功能

  1. 01涵盖 19 类代码冗余模式
  2. 02坚持清理不改变行为
  3. 03按差异范围检查
  4. 04提供验证指引
  5. 05发现项引用模式编号

适用场景

  1. 01清理差异中的冗余注释和名称
  2. 02识别多余封装或防御处理
  3. 03区分清理与行为变更

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/howells/skills --skill unslop

安装后在 Claude Code 中输入/unslop确认已加载。

附带文件agents/openai.yaml

详细介绍

Unslop

deslop 在代码领域的对应工具。理念相同,处理对象不同:deslop 去除写作中的特征,unslop 去除代码中的特征。

Agent 编写的代码带有一种标记。不是 Bug——它能通过类型检查,测试也能通过,功能正常。代码里堆满了繁文缛节,因为模型想面面俱到,而不是因为实际需要;代价则落在下一个阅读它的人身上。

每次判断都遵循这条规则:这里的改动不会改变行为。 去掉某个特征后,测试仍能原样通过。如果清理会改变程序的行为,那就不属于此技能的范围——隐藏失败的回退逻辑属于 fail-fast,大到难以在脑中掌握的文件属于 componentize。说明情况,然后继续,不要把行为变更偷偷塞进整理工作中。

判断某个东西是否属于特征之前,先阅读周边代码。局部惯例和抽象的目的都很重要:即使包装函数只有一个调用方,也可能为有意义的操作命名,或保留有用的边界。目录列出的是值得调查的模式,不是自动删除的清单。优先采用能消除已确认噪音的最小改动;不要为了迎合自己的喜好而重写相邻代码。

特征

在发现结果中引用编号,让审阅者能看出所指的是哪种模式,而不只是具体实例。

注释和名称

  1. 叙述性注释。 // loop over the users、// Phase 1: build the cards。代码已经说明了这些内容。一个步骤所需的说明,只需断言文本或日志行。
  2. 重复签名内容的文档注释。 "@param userId - the user id"。如果文档没有补充类型中没有的信息,就删掉。如果符号已导出,且确实需要有用的悬停提示,那属于 marginalia,而不是删除。
  3. 注释掉的备选方案。 留着“以防万一”的另一种做法。Git 里有。
  4. 过度明确的名称。 userDataObject、handleClickEventHandler、configurationOptionsMap,或在类型名称中重复表达的信息:UserShape、UserInterface。说出名词即可。

繁文缛节

  1. 只有一个调用方的包装层。 函数、类或模块只在一个地方被调用,而且没有增加任何内容。将其内联。
  2. 只有一种实现的接口。 为从未出现的第二种实现而编写。
  3. 没人传入的选项。 默认值是唯一使用值的参数、只有一个有效键的配置对象、只有一种状态的功能开关。
  4. 为对称而对称。 空的 else、不可能到达的 default:、什么都不做的 finally、只负责等待的包装层。
  5. 只保存一个常量的文件。 除非边界本身有意义,否则就把它放到使用它的地方。

防御性噪音 10. 多余的防御性处理。 对不可能缺失的值进行空值检查;编译器已经完成类型收窄后再做运行时 typeof 检查;或只会重新抛出相同错误的 try/catch。先确认这条路径可信;单靠类型注解无法验证运行时输入。保留边界验证,以及能补充有意义的上下文或执行清理的捕获逻辑。会吞掉错误或提供回退值的捕获逻辑,若移除后会改变行为,则归入 fail-fast。 11. 多余的断言。 仅用于消除类型错误的 as any、as unknown as T、本可以通过诚实地收窄类型来避免的非空断言 !,以及先将已知值赋给 unknown 或宽泛记录类型、再断言回原有类型的写法。应使用正确的类型或收窄类型,而不是再添加断言。如果修复类型不匹配需要改变运行时行为,则应报告。 12. 重复实现标准库功能。 已有库函数可用,却又手写了 groupBy、chunk、debounce 或日期格式化函数。只有在确认其对调用方的语义等效后才替换,包括时序、默认值、错误、区域设置和边缘情况;否则应报告。 13. 多余的异步处理。 只有在追踪调用方并保留 Promise 返回值、拒绝行为和调度方式后,才能移除异步语法。没有 await 的 async 函数仍会返回 Promise,并将抛出的异常转换为拒绝;等待普通值仍会挂起后续执行。不能仅因为语法看起来多余,就认定它们确实多余。对不确定的改动应报告,而不是猜测。

输出与遗留问题

面向用户、脚本或监控系统的 CLI 输出、错误消息和日志都是可观察契约。除非另有要求,否则应保持不变。不要为了清理代码而更新快照;无法确认兼容性的改动应予以报告。

  1. 带装饰的控制台输出。 日志和 CLI 输出中的 Emoji、制表符边框、✅/❌ 状态表演以及横幅标题。
  2. 祝贺语。 “Successfully completed!”、“All done!”。应报告结果,或者不作说明。
  3. 复述堆栈的错误消息。 说明发生了什么错误,以及调用方应如何处理;堆栈信息已经包含在内。
  4. 死代码脚手架。 无人负责的 TODO 占位内容、示例代码块,以及任何混入生产路径的 mock、stub 或 fixture。
  5. 仅断言偶然性 mock 细节的测试。 检查断言要保护什么:调用次数或参数可能是实际契约,例如防止重复写入。应保留有意义的交互检查。对于无法证明任何行为的测试,应予以报告,而不是悄悄删除覆盖,或在请求范围之外开始重写测试。

控制流

  1. 不必要的嵌套。 新增的 if/else 层级若可通过提前返回让代码更易理解,就应避免。只有在执行顺序、副作用、清理和返回值都保持不变时才能扁平化。当嵌套能清晰表达逻辑或符合周边代码风格时,应保留。

步骤

让工作流程与差异范围相称。小型清理应简要说明范围、理由和验证方式;可以不单独列出任务清单。

  1. 确定范围。 默认以差异为准:分支相对于其合并基准的变更,或工作树中的变更。全面检查整个仓库是另一项工作,需要明确说明,因为这会触及无人要求你修改的代码。
  2. 选择相关验证。 指明可能发生变化的行为,并使用能够捕捉问题的最精简现有检查。对于仅涉及注释的变更,检查差异和适用的文档检查。记录无关的基线失败,并继续开展安全且独立的工作。保留仓库要求的最终检查;不要启动覆盖整个工作区的逐级检查。
  3. 逐项对照目录检查差异。 阅读附近的代码以了解局部惯例,然后为每个已确认的实例记录一条发现,每条都注明提示编号和 file:line。阅读时不要改写——边扫描边编辑会打断思路,还会让你开始擅自改进内容。
  4. 将发现分为移除和转交。 移除项是任何不改变行为的内容。转交项是任何会改变程序行为的内容,并注明负责处理它的技能。绝不要直接处理转交项。
  5. 执行移除,然后验证受影响的契约。 运行选定的检查,并在相关时检查返回值、副作用和时序。测试通过有助于支持行为等价,但不能证明行为等价。调查结果变化,区分偶发失败和既有失败,并撤销任何会改变行为的移除。
  6. 报告。 对于小型清理,用一到三句话说明改动内容、是否有事项转交给其他地方,以及检查在改动前后的结果。必要时附上提示编号和文件位置来说明发现;只有在用户要求审计,或差异较大且计数有帮助时,才按提示编号统计数量。

这不是什么

  • 不是风格检查。格式应遵循仓库的格式化工具。
  • 不是重构。在模块间移动逻辑或提取程序包属于 componentize。
  • 不是加固。删除回退机制,让程序明确报错属于 fail-fast,而且这是有意改变行为。
  • 不是代码检查工具。机器可以对每个文件检查的规则——没有说明理由的类型断言、参数和返回值中的 unknown、模块模拟、通过 Reflect 访问属性——都应放在仓库的 lint 配置中;howells-lint 会设置 @howells/lint,其中包含这些规则。如果 lint 没有加载这些规则,应将其转交处理,而不是逐处手动检查。
  • 不是代码审查。正确性缺陷属于另一项工作;如果发现此类问题,应报告并保持原样。

评论

支持 Markdown