ai-slop-cleaner

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

AI Slop Cleaner

AI Slop Cleaner

AI code generation produces working code. It also produces unnecessary code alongside it. This skill removes the unnecessary parts while keeping everything that matters.
AI代码生成会产出可运行的代码,但同时也会附带不必要的代码。本工具会移除这些不必要的部分,同时保留所有关键功能。

What Is "AI Slop"?

什么是「AI Slop」?

AI slop is code that:
  • Works, but shouldn't exist
  • Adds complexity without adding value
  • Was clearly generated to pad a response rather than solve a problem
  • Suggests the author wasn't thinking, just generating
Common slop categories and their signals:
CategorySignal
Dead importsImported but never referenced in the file
Unused variablesDeclared, never read
Commented-out codeBlocks of
// old code
or
/* removed */
Debug remnants
console.log
,
print()
,
debugger
,
fmt.Println
Obvious comments
// increment counter
above
count++
Redundant JSDoc
@param name - the name
above
name: string
Premature abstractionsA factory that creates exactly one thing
One-use helpersPrivate function called exactly once, trivially inlinable
Overly generic types
<T extends object>
when
T
is always
User
Over-parameterized
fn(a, b, c, d, e)
where 4 params never vary
Unreachable branches
if (false)
or
if (isLoggedIn && !isLoggedIn)
Speculative featuresCode paths for requirements that don't exist
Copy-paste duplicationTwo blocks identical except one variable name
Placeholder remnants
TODO: implement
, lorem ipsum, example data in prod
AI Slop指的是这类代码:
  • 可以运行,但本不该存在
  • 增加复杂度却没有任何价值
  • 明显是为了凑字数而非解决问题生成的
  • 表明作者没有思考,只是单纯生成代码
常见冗余代码类别及特征:
类别特征
无用导入已导入但在文件中从未被引用
未使用变量已声明但从未被读取
注释掉的代码
// old code
/* removed */
这类代码块
调试残留
console.log
print()
debugger
fmt.Println
冗余注释
count++
上方的
// increment counter
这类注释
冗余JSDoc
name: string
上方的
@param name - the name
这类注释
过早抽象只创建一个实例的工厂类
单次使用辅助函数仅被调用一次、可轻松内联的私有函数
过度泛型类型
T
始终是
User
时使用
<T extends object>
参数过多
fn(a, b, c, d, e)
中有4个参数从未变化
不可达分支
if (false)
if (isLoggedIn && !isLoggedIn)
这类条件
投机性功能针对不存在的需求编写的代码路径
复制粘贴重复代码除了一个变量名外完全相同的两个代码块
占位符残留生产环境中的
TODO: implement
、乱码文本、示例数据

The Prime Directive

首要原则

Tests are sacred. Never clean test files.
Tests exist to protect behavior. Any cleanup that breaks a test reveals that the "slop" was actually load-bearing. That is good information. The test wins.
测试是神圣不可侵犯的。绝不要清理测试文件。
测试的存在是为了保护功能。任何导致测试失败的清理操作,都说明所谓的“冗余代码”实际上是必要的。这是很重要的信息,测试永远优先。

Regression-Safe Workflow (Non-Negotiable)

回归安全工作流(不可协商)

BEFORE ANYTHING: Run full test suite → all tests must pass (baseline)

FOR EACH PASS:
  1. Identify targets for this pass category
  2. Apply cleanup
  3. Run tests
  4. If tests pass: keep cleanup, continue
  5. If tests fail: git checkout -- . (revert), skip this pass category
  6. Log what was reverted and why

AFTER ALL PASSES: Run full test suite → confirm all tests still pass
Report: lines removed, files touched, passes skipped, reason for each skip
Never batch multiple pass categories together. If combined changes break a test, you cannot know which change caused it.
开始前:运行完整测试套件 → 所有测试必须通过(基准线)

每一轮清理:
  1. 确定本轮要处理的类别目标
  2. 执行清理操作
  3. 运行测试
  4. 如果测试通过:保留清理结果,继续下一轮
  5. 如果测试失败:执行git checkout -- .(回退),跳过本轮类别
  6. 记录回退内容及原因

所有轮次完成后:运行完整测试套件 → 确认所有测试仍通过
生成报告:移除的行数、修改的文件数、跳过的轮次、每轮跳过的原因
不要将多个类别的清理合并进行。如果组合修改导致测试失败,你将无法确定是哪一项修改引发的问题。

The 7 Cleaning Passes

7轮清理步骤

Pass 1: Dead Imports and Unused Variables

第一轮:无用导入与未使用变量

Risk: Very Low
What to remove:
  • Import statements where the imported name never appears in the file body
  • Variables declared with
    let
    /
    const
    /
    var
    that are never read after assignment
  • Function parameters that are never referenced inside the function body (TypeScript: prefix with
    _
    )
Before:
typescript
import { useState, useEffect, useCallback, useMemo } from 'react'
import { formatDate } from '@/lib/utils'
import { ApiClient } from '@/lib/api'

export function UserCard({ user }) {
  const [count, setCount] = useState(0)
  const formatted = formatDate(user.createdAt)

  return <div>{user.name}</div>
}
After:
typescript
import { useState } from 'react'
import { formatDate } from '@/lib/utils'

export function UserCard({ user }) {
  const [count, setCount] = useState(0)
  const formatted = formatDate(user.createdAt)

  return <div>{user.name}</div>
}
Note:
count
,
setCount
, and
formatted
are still present because they may be used elsewhere in a larger component. Pass 1 only removes imports.
风险:极低
需要移除的内容:
  • 导入的名称在文件主体中从未出现过的导入语句
  • 使用
    let
    /
    const
    /
    var
    声明但赋值后从未被读取的变量
  • 函数体内从未被引用的函数参数(TypeScript:可添加
    _
    前缀标记)
之前的代码:
typescript
import { useState, useEffect, useCallback, useMemo } from 'react'
import { formatDate } from '@/lib/utils'
import { ApiClient } from '@/lib/api'

export function UserCard({ user }) {
  const [count, setCount] = useState(0)
  const formatted = formatDate(user.createdAt)

  return <div>{user.name}</div>
}
之后的代码:
typescript
import { useState } from 'react'
import { formatDate } from '@/lib/utils'

export function UserCard({ user }) {
  const [count, setCount] = useState(0)
  const formatted = formatDate(user.createdAt)

  return <div>{user.name}</div>
}
注意:
count
setCount
formatted
仍然保留,因为它们可能在更大的组件中的其他地方被使用。第一轮仅移除无用导入。

Pass 2: Commented-Out Code and Debug Statements

第二轮:注释代码与调试语句

Risk: Very Low
What to remove:
  • Any block of commented-out code that is not an active TODO or architectural note
  • console.log
    ,
    console.debug
    ,
    console.warn
    (unless it is a legitimate error logger)
  • debugger
    statements
  • print()
    in Python when not serving as actual program output
  • fmt.Println
    in Go debug instrumentation
Before:
typescript
async function processOrder(orderId: string) {
  console.log('processing order', orderId)
  const order = await db.orders.findById(orderId)
  // const cached = await cache.get(orderId)
  // if (cached) return cached
  console.log('order fetched:', order)

  const result = await payments.charge(order)
  // TODO: add retry logic here
  // console.log('charge result', result)

  return result
}
After:
typescript
async function processOrder(orderId: string) {
  const order = await db.orders.findById(orderId)

  const result = await payments.charge(order)
  // TODO: add retry logic here

  return result
}
Rule:
// TODO:
comments are preserved. They are documentation of known gaps, not slop.
风险:极低
需要移除的内容:
  • 任何非活跃TODO或架构说明的注释代码块
  • console.log
    console.debug
    console.warn
    (合法错误日志除外)
  • debugger
    语句
  • Python中不作为实际程序输出的
    print()
  • Go调试工具中的
    fmt.Println
之前的代码:
typescript
async function processOrder(orderId: string) {
  console.log('processing order', orderId)
  const order = await db.orders.findById(orderId)
  // const cached = await cache.get(orderId)
  // if (cached) return cached
  console.log('order fetched:', order)

  const result = await payments.charge(order)
  // TODO: add retry logic here
  // console.log('charge result', result)

  return result
}
之后的代码:
typescript
async function processOrder(orderId: string) {
  const order = await db.orders.findById(orderId)

  const result = await payments.charge(order)
  // TODO: add retry logic here

  return result
}
规则:
// TODO:
注释会被保留。它们是已知缺口的文档,不属于冗余代码。

Pass 3: Obvious Comments and Redundant Documentation

第三轮:冗余注释与多余文档

Risk: Low
What to remove:
  • Comments that restate the code in plain English without adding context
  • JSDoc
    @param
    blocks that just repeat the parameter name and type (TypeScript already says this)
  • Section dividers that add no structure (
    // ===== COMPONENT =====
    )
  • End-of-block comments (
    } // end if
    ,
    } // end for
    )
Before:
typescript
/**
 * Gets a user by ID.
 * @param id - the user ID
 * @param db - the database instance
 * @returns the user object
 */
async function getUserById(id: string, db: Database): Promise<User> {
  // Query the database for the user
  const user = await db.users.findById(id)

  // Return the user
  return user
} // end getUserById
After:
typescript
async function getUserById(id: string, db: Database): Promise<User> {
  return db.users.findById(id)
}
Keep comments that explain WHY (business rules, performance choices, known gotchas). Remove comments that explain WHAT (the code already says what).
风险:低
需要移除的内容:
  • 用直白语言重述代码但未添加上下文的注释
  • 仅重复参数名称和类型的JSDoc
    @param
    块(TypeScript已明确标注)
  • 未添加结构的分隔符(如
    // ===== COMPONENT =====
  • 块结束注释(如
    } // end if
    } // end for
之前的代码:
typescript
/**
 * Gets a user by ID.
 * @param id - the user ID
 * @param db - the database instance
 * @returns the user object
 */
async function getUserById(id: string, db: Database): Promise<User> {
  // Query the database for the user
  const user = await db.users.findById(id)

  // Return the user
  return user
} // end getUserById
之后的代码:
typescript
async function getUserById(id: string, db: Database): Promise<User> {
  return db.users.findById(id)
}
保留解释“为什么”的注释(如业务规则、性能选择、已知陷阱)。移除解释“是什么”的注释(代码本身已经说明)。

Pass 4: Dead Code (Unreachable Branches)

第四轮:无用代码(不可达分支)

Risk: Medium — Run tests immediately after
What to remove:
  • Conditions that are always true or always false
  • Code after unconditional
    return
    ,
    throw
    , or
    break
  • Else branches of conditions that always throw in the if block
Before:
typescript
function getStatus(user: User): string {
  if (user.role === 'admin' || user.role === 'admin') {
    return 'ADMIN'
  }

  if (user.isActive) {
    return 'ACTIVE'
  } else {
    return 'INACTIVE'
  }

  // This never runs
  return 'UNKNOWN'
}
After:
typescript
function getStatus(user: User): string {
  if (user.role === 'admin') {
    return 'ADMIN'
  }

  return user.isActive ? 'ACTIVE' : 'INACTIVE'
}
Do not remove branches that look unreachable but depend on runtime data you cannot verify statically. When uncertain, leave it.
风险:中等 — 清理后立即运行测试
需要移除的内容:
  • 始终为真或始终为假的条件
  • 无条件
    return
    throw
    break
    后的代码
  • if块中始终抛出错误的else分支
之前的代码:
typescript
function getStatus(user: User): string {
  if (user.role === 'admin' || user.role === 'admin') {
    return 'ADMIN'
  }

  if (user.isActive) {
    return 'ACTIVE'
  } else {
    return 'INACTIVE'
  }

  // This never runs
  return 'UNKNOWN'
}
之后的代码:
typescript
function getStatus(user: User): string {
  if (user.role === 'admin') {
    return 'ADMIN'
  }

  return user.isActive ? 'ACTIVE' : 'INACTIVE'
}
不要移除那些看似不可达但依赖你无法静态验证的运行时数据的分支。不确定时,保留该代码。

Pass 5: Premature Abstractions (Inline One-Use Helpers)

第五轮:过早抽象(内联单次使用辅助函数)

Risk: Medium — Run tests immediately after
What to inline:
  • Private/internal functions called exactly once
  • Wrapper functions that add no logic (just forward all arguments)
  • Intermediate variables assigned once and used once on the next line
Before:
typescript
function formatUserDisplayName(user: User): string {
  return `${user.firstName} ${user.lastName}`.trim()
}

function renderUserCard(user: User) {
  const displayName = formatUserDisplayName(user)
  return `<div class="card">${displayName}</div>`
}
After (if
formatUserDisplayName
is only called from
renderUserCard
):
typescript
function renderUserCard(user: User) {
  const displayName = `${user.firstName} ${user.lastName}`.trim()
  return `<div class="card">${displayName}</div>`
}
Do NOT inline if:
  • The function is exported (public API)
  • The function is called from more than one place
  • The function name serves as meaningful documentation of intent
  • The function contains error handling that would add visual noise when inlined
风险:中等 — 清理后立即运行测试
需要内联的内容:
  • 仅被调用一次的私有/内部函数
  • 未添加任何逻辑的包装函数(仅转发所有参数)
  • 赋值一次且仅在下一行使用的中间变量
之前的代码:
typescript
function formatUserDisplayName(user: User): string {
  return `${user.firstName} ${user.lastName}`.trim()
}

function renderUserCard(user: User) {
  const displayName = formatUserDisplayName(user)
  return `<div class="card">${displayName}</div>`
}
之后的代码(如果
formatUserDisplayName
仅被
renderUserCard
调用):
typescript
function renderUserCard(user: User) {
  const displayName = `${user.firstName} ${user.lastName}`.trim()
  return `<div class="card">${displayName}</div>`
}
以下情况请勿内联:
  • 函数是导出的(公共API)
  • 函数被多个地方调用
  • 函数名称是对意图的有意义文档
  • 函数包含内联后会增加视觉干扰的错误处理

Pass 6: Duplication Consolidation

第六轮:重复代码合并

Risk: Medium-High — Run tests immediately after each consolidation
What to consolidate:
  • Two or more blocks with identical structure and only one variable difference
  • Repeated conditional checks that could be extracted to a guard function
  • Multiple switch/if-else blocks with the same cases in different files
Before:
typescript
// In UserService
async function getActiveUsers() {
  const users = await db.query(
    'SELECT * FROM users WHERE status = $1 AND deleted_at IS NULL',
    ['active']
  )
  return users.rows
}

// In AdminService (same file or different file)
async function getActiveAdmins() {
  const admins = await db.query(
    'SELECT * FROM users WHERE status = $1 AND deleted_at IS NULL AND role = $2',
    ['active', 'admin']
  )
  return admins.rows
}
After:
typescript
async function getActiveUsers(role?: string) {
  const params: unknown[] = ['active']
  let sql = 'SELECT * FROM users WHERE status = $1 AND deleted_at IS NULL'

  if (role) {
    sql += ' AND role = $2'
    params.push(role)
  }

  const result = await db.query(sql, params)
  return result.rows
}
Be careful: consolidation that requires complex parameterization may make code harder to understand. If the consolidated version is more complex than the two originals, leave them separate.
风险:中高 — 每次合并后立即运行测试
需要合并的内容:
  • 两个或多个结构相同仅一个变量不同的代码块
  • 可提取为守卫函数的重复条件检查
  • 不同文件中具有相同分支的多个switch/if-else块
之前的代码:
typescript
// In UserService
async function getActiveUsers() {
  const users = await db.query(
    'SELECT * FROM users WHERE status = $1 AND deleted_at IS NULL',
    ['active']
  )
  return users.rows
}

// In AdminService (same file or different file)
async function getActiveAdmins() {
  const admins = await db.query(
    'SELECT * FROM users WHERE status = $1 AND deleted_at IS NULL AND role = $2',
    ['active', 'admin']
  )
  return admins.rows
}
之后的代码:
typescript
async function getActiveUsers(role?: string) {
  const params: unknown[] = ['active']
  let sql = 'SELECT * FROM users WHERE status = $1 AND deleted_at IS NULL'

  if (role) {
    sql += ' AND role = $2'
    params.push(role)
  }

  const result = await db.query(sql, params)
  return result.rows
}
注意:需要复杂参数化的合并可能会让代码更难理解。如果合并后的版本比两个原始版本更复杂,保留它们的独立状态。

Pass 7: Over-Engineering Simplification

第七轮:过度工程简化

Risk: High — High scrutiny, run tests after every individual change
What to simplify:
  • Factory pattern used to create exactly one concrete type
  • Strategy pattern with exactly one strategy
  • Abstract base class with exactly one implementation
  • Generic type parameter constrained so tightly it could be a concrete type
Before:
typescript
interface DataProcessor<T extends BaseData> {
  process(data: T): ProcessedData<T>
  validate(data: T): ValidationResult
}

class UserDataProcessorFactory {
  create(): DataProcessor<UserData> {
    return new UserDataProcessor()
  }
}

class UserDataProcessor implements DataProcessor<UserData> {
  process(data: UserData): ProcessedData<UserData> {
    return { ...data, processed: true }
  }

  validate(data: UserData): ValidationResult {
    return { valid: !!data.id }
  }
}
After (if only UserData ever flows through this):
typescript
function processUserData(data: UserData) {
  if (!data.id) throw new Error('Invalid user data: missing id')
  return { ...data, processed: true }
}
Rule for Pass 7: If you need more than 2 minutes to understand why an abstraction exists, and there is only one concrete case, remove the abstraction. If you find yourself unsure whether it is load-bearing, leave it. Pass 7 is optional.
风险:高 — 严格审查,每次单独修改后运行测试
需要简化的内容:
  • 用于创建唯一具体类型的工厂模式
  • 仅有一种策略的策略模式
  • 仅有一种实现的抽象基类
  • 约束过紧可替换为具体类型的泛型参数
之前的代码:
typescript
interface DataProcessor<T extends BaseData> {
  process(data: T): ProcessedData<T>
  validate(data: T): ValidationResult
}

class UserDataProcessorFactory {
  create(): DataProcessor<UserData> {
    return new UserDataProcessor()
  }
}

class UserDataProcessor implements DataProcessor<UserData> {
  process(data: UserData): ProcessedData<UserData> {
    return { ...data, processed: true }
  }

  validate(data: UserData): ValidationResult {
    return { valid: !!data.id }
  }
}
之后的代码(如果仅有UserData会流经此处):
typescript
function processUserData(data: UserData) {
  if (!data.id) throw new Error('Invalid user data: missing id')
  return { ...data, processed: true }
}
第七轮规则:如果你需要超过2分钟才能理解某个抽象存在的原因,且仅有一个具体案例,移除该抽象。如果你不确定它是否是必要的,保留它。第七轮为可选步骤。

What Is Never Cleaned

绝不清理的内容

TargetReason
Test files (
*.test.*
,
*.spec.*
,
__tests__/
)
Tests are sacred
Public API signaturesBreaks callers
Error handling at system boundaries (API routes, top-level handlers)Defense-in-depth
Comments explaining regulatory/compliance requirementsLegal context
Feature flagsMay be toggled at runtime
Anything marked
// KEEP
or
// intentional
Explicit author decision
目标原因
测试文件(
*.test.*
*.spec.*
__tests__/
测试是神圣的
公共API签名会破坏调用方
系统边界处的错误处理(API路由、顶级处理器)深度防御需求
解释监管/合规要求的注释法律上下文需求
功能开关可能在运行时切换
标记为
// KEEP
// intentional
的内容
作者明确的决策

.slopignore File

.slopignore 文件

Place at project root to exclude paths:
undefined
放置在项目根目录以排除指定路径:
undefined

.slopignore

.slopignore

src/legacy/ # Old code being migrated, don't touch src/generated/ # Auto-generated, cleaned by generator vendor/ # Third-party code
undefined
src/legacy/ # 正在迁移的旧代码,请勿触碰 src/generated/ # 自动生成的代码,由生成器负责清理 vendor/ # 第三方代码
undefined

Metrics Report

指标报告

After all passes complete, output:
AI Slop Cleaner Report
======================
Files touched:          12
Lines removed:          147
Lines remaining:        1,843
Reduction:              7.4%

Pass results:
  Pass 1 (Dead imports):       DONE — 23 lines removed
  Pass 2 (Debug code):         DONE — 18 lines removed
  Pass 3 (Obvious comments):   DONE — 41 lines removed
  Pass 4 (Dead code):          DONE — 12 lines removed
  Pass 5 (One-use helpers):    DONE — 31 lines removed
  Pass 6 (Duplication):        SKIPPED — test failed after consolidation (UserService)
  Pass 7 (Over-engineering):   DONE — 22 lines removed

Skipped details:
  Pass 6 reverted: UserService query consolidation broke getUsersByStatus test.
  Root cause: test was asserting on the exact SQL string. Left original.

Test status: ALL PASSING (127/127)
所有轮次完成后,输出如下报告:
AI Slop Cleaner Report
======================
Files touched:          12
Lines removed:          147
Lines remaining:        1,843
Reduction:              7.4%

Pass results:
  Pass 1 (Dead imports):       DONE — 23 lines removed
  Pass 2 (Debug code):         DONE — 18 lines removed
  Pass 3 (Obvious comments):   DONE — 41 lines removed
  Pass 4 (Dead code):          DONE — 12 lines removed
  Pass 5 (One-use helpers):    DONE — 31 lines removed
  Pass 6 (Duplication):        SKIPPED — test failed after consolidation (UserService)
  Pass 7 (Over-engineering):   DONE — 22 lines removed

Skipped details:
  Pass 6 reverted: UserService query consolidation broke getUsersByStatus test.
  Root cause: test was asserting on the exact SQL string. Left original.

Test status: ALL PASSING (127/127)

Integration with refactor-cleaner Agent

与refactor-cleaner Agent的集成

AI slop cleaner runs at the code level (syntactic cleanup). The refactor-cleaner agent runs at the architecture level (structural refactoring). Run this skill first, then refactor-cleaner if structural improvement is needed.
Order:
1. ai-slop-cleaner (remove the noise)
2. code-reviewer (verify quality after cleanup)
3. refactor-cleaner (structural improvements if needed)
4. verifier (final gate)
AI Slop Cleaner在代码层面运行(语法清理)。refactor-cleaner Agent在架构层面运行(结构重构)。先运行本工具,若需要结构改进再运行refactor-cleaner。
顺序:
1. ai-slop-cleaner(移除冗余代码)
2. code-reviewer(验证清理后的代码质量)
3. refactor-cleaner(如需结构改进)
4. verifier(最终验证)

Automatic Trigger

自动触发

This skill is automatically triggered after:
  • kraken agent completes a feature implementation
  • spark agent completes a fix
  • Any agent produces more than 200 new lines of code
The trigger runs Pass 1 and Pass 2 only by default (very low risk). Passes 3-7 require explicit activation or a
/clean
command.

Remember: The goal is not minimum lines of code. The goal is maximum clarity per line. If removing something makes the code harder to understand, put it back.
本工具会在以下场景自动触发:
  • kraken Agent完成功能实现后
  • spark Agent完成修复后
  • 任何Agent生成超过200行新代码后
默认仅自动运行第一轮和第二轮(风险极低)。第三至第七轮需要显式激活或使用
/clean
命令。

提醒:目标并非最小化代码行数,而是最大化每行代码的清晰度。如果移除某部分内容会让代码更难理解,请恢复它。