智能代码清理Skill clean

这是一个智能代码清理技能,用于自动化检测和删除软件项目中的过时代码工件、死代码和漂移文档,提高代码维护效率和开发流程优化。支持干运行、焦点区域清理和用户确认,确保安全执行。关键词:代码清理、死代码检测、git历史分析、自动化维护、DevOps、代码优化、软件维护。

DevOps 0 次安装 0 次浏览 更新于 3/16/2026

name: clean description: 智能代码清理,具备主线检测、陈旧工件发现和安全执行功能。支持定向清理和确认。 argument-hint: “[–dry-run] [–focus=<area>]”

工作流清理命令

概述

基于证据的智能清理命令。通过主线分析系统性地识别陈旧工件,发现漂移,并安全移除未使用的会话、文档和死代码。

核心工作流:检测主线 → 发现漂移 → 确认 → 暂存 → 执行

目标清理

焦点区域:$FOCUS(如果未指定,则为整个项目) 模式:$ARGUMENTS

  • --dry-run:预览清理而不执行
  • --focus:焦点区域(模块或路径)

执行流程

阶段 0:初始化
   ├─ 解析参数(--dry-run, FOCUS)
   ├─ 设置会话文件夹
   └─ 初始化工具函数

阶段 1:主线检测
   ├─ 分析 git 历史(30 天)
   ├─ 识别核心模块(高提交频率)
   ├─ 映射活跃与陈旧分支
   └─ 构建主线配置文件

阶段 2:漂移发现(子代理)
   ├─ 使用 cli-explore-agent 角色启动代理
   ├─ 扫描工作流会话中的孤立工件
   ├─ 识别偏离主线的文档
   ├─ 检测死代码和未使用的导出
   └─ 生成清理清单

阶段 3:确认
   ├─ 验证清单模式
   ├─ 按类别显示清理摘要
   ├─ ASK_USER:选择类别和风险级别
   └─ 如果是 --dry-run,则退出

阶段 4:执行
   ├─ 验证路径(安全检查)
   ├─ 暂存删除(移动到 .trash)
   ├─ 更新清单
   ├─ 永久删除
   └─ 报告结果

实现

阶段 0:初始化

步骤 0:确定项目根目录

检测项目根目录,确保 .workflow/ 产物位置正确:

PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)

优先通过 git 获取仓库根目录;非 git 项目回退到 pwd 取当前绝对路径。 存储为 {projectRoot},后续所有 .workflow/ 路径必须以此为前缀。

const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()

// 解析参数
const args = "$ARGUMENTS"
const isDryRun = args.includes('--dry-run')
const focusMatch = args.match(/FOCUS="([^"]+)"/)
const focusArea = focusMatch ? focusMatch[1] : "$FOCUS" !== "$" + "FOCUS" ? "$FOCUS" : null

// 会话设置
const dateStr = getUtc8ISOString().substring(0, 10)
const sessionId = `clean-${dateStr}`
const sessionFolder = `${projectRoot}/.workflow/.clean/${sessionId}`
const trashFolder = `${sessionFolder}/.trash`
const projectRoot = bash('git rev-parse --show-toplevel 2>/dev/null || pwd').trim()

bash(`mkdir -p ${sessionFolder}`)
bash(`mkdir -p ${trashFolder}`)

// 工具函数
function fileExists(p) {
  try { return bash(`test -f "${p}" && echo "yes"`).includes('yes') } catch { return false }
}

function dirExists(p) {
  try { return bash(`test -d "${p}" && echo "yes"`).includes('yes') } catch { return false }
}

function validatePath(targetPath) {
  if (targetPath.includes('..')) return { valid: false, reason: '路径遍历' }

  const allowed = ['.workflow/', '.claude/rules/tech/', 'src/']
  const dangerous = [/^\//, /^C:\\Windows/i, /node_modules/, /\.git$/]

  if (!allowed.some(p => targetPath.startsWith(p))) {
    return { valid: false, reason: '超出允许目录' }
  }
  if (dangerous.some(p => p.test(targetPath))) {
    return { valid: false, reason: '危险模式' }
  }
  return { valid: true }
}

阶段 1:主线检测

// 检查 git 仓库
const isGitRepo = bash('git rev-parse --git-dir 2>/dev/null && echo "yes"').includes('yes')

let mainlineProfile = {
  coreModules: [],
  activeFiles: [],
  activeBranches: [],
  staleThreshold: { sessions: 7, branches: 30, documents: 14 },
  isGitRepo,
  timestamp: getUtc8ISOString()
}

if (isGitRepo) {
  // 按目录的提交频率(最近 30 天)
  const freq = bash('git log --since="30 days ago" --name-only --pretty=format: | grep -v "^$" | cut -d/ -f1-2 | sort | uniq -c | sort -rn | head -20')

  // 解析核心模块(>5 次提交)
  mainlineProfile.coreModules = freq.trim().split('
')
    .map(l => l.trim().match(/^(\d+)\s+(.+)$/))
    .filter(m => m && parseInt(m[1]) >= 5)
    .map(m => m[2])

  // 最近分支
  const branches = bash('git for-each-ref --sort=-committerdate refs/heads/ --format="%(refname:short)" | head -10')
  mainlineProfile.activeBranches = branches.trim().split('
').filter(Boolean)
}

Write(`${sessionFolder}/mainline-profile.json`, JSON.stringify(mainlineProfile, null, 2))

阶段 2:漂移发现

let exploreAgent = null

try {
  // 启动 cli-explore-agent
  exploreAgent = spawn_agent({
    message: `
## 任务分配

### 强制初始步骤
1. 阅读:~/.codex/agents/cli-explore-agent.md
2. 阅读:${projectRoot}/.workflow/project-tech.json(如果存在)

## 任务目标
发现陈旧工件以进行清理。

## 上下文
- 会话:${sessionFolder}
- 焦点:${focusArea || '整个项目'}

## 发现类别

### 1. 陈旧工作流会话
扫描:${projectRoot}/.workflow/active/WFS-*, ${projectRoot}/.workflow/archives/WFS-*, ${projectRoot}/.workflow/.lite-plan/*, ${projectRoot}/.workflow/.debug/DBG-*
标准:无修改 >7 天 + 无相关 git 提交

### 2. 漂移文档
扫描:.claude/rules/tech/*, ${projectRoot}/.workflow/.scratchpad/*
标准:>30% 破损引用指向不存在的文件

### 3. 死代码
扫描:未使用的导出、孤立文件(未被任何地方导入)
标准:导入图中无导入者

## 输出
写入:${sessionFolder}/cleanup-manifest.json

格式:
{
  "generated_at": "ISO",
  "discoveries": {
    "stale_sessions": [{ "path": "...", "age_days": N, "reason": "...", "risk": "low|medium|high" }],
    "drifted_documents": [{ "path": "...", "drift_percentage": N, "reason": "...", "risk": "..." }],
    "dead_code": [{ "path": "...", "type": "orphan_file", "reason": "...", "risk": "..." }]
  },
  "summary": { "total_items": N, "by_category": {...}, "by_risk": {...} }
}
`
  })

  // 等待并处理超时
  let result = wait({ ids: [exploreAgent], timeout_ms: 600000 })

  if (result.timed_out) {
    send_input({ id: exploreAgent, message: '现在完成并写入 cleanup-manifest.json。' })
    result = wait({ ids: [exploreAgent], timeout_ms: 300000 })
    if (result.timed_out) throw new Error('代理超时')
  }

  if (!fileExists(`${sessionFolder}/cleanup-manifest.json`)) {
    throw new Error('清单未生成')
  }

} finally {
  if (exploreAgent) close_agent({ id: exploreAgent })
}

阶段 3:确认

// 加载并验证清单
const manifest = JSON.parse(Read(`${sessionFolder}/cleanup-manifest.json`))

// 显示摘要
console.log(`
## 清理发现报告

| 类别 | 数量 | 风险 |
|----------|-------|------|
| 会话 | ${manifest.summary.by_category.stale_sessions} | ${getRiskSummary('sessions')} |
| 文档 | ${manifest.summary.by_category.drifted_documents} | ${getRiskSummary('documents')} |
| 死代码 | ${manifest.summary.by_category.dead_code} | ${getRiskSummary('code')} |

**总计**:${manifest.summary.total_items} 个项目
`)

// 干运行退出
if (isDryRun) {
  console.log(`
**干运行模式**:未进行任何更改。
清单:${sessionFolder}/cleanup-manifest.json
  `)
  return
}

// 用户确认
const selection = ASK_USER([
  {
    id: "categories", type: "multi-select",
    prompt: "清理哪些类别?",
    options: [
      { label: "会话", description: `${manifest.summary.by_category.stale_sessions} 个陈旧会话` },
      { label: "文档", description: `${manifest.summary.by_category.drifted_documents} 个漂移文档` },
      { label: "死代码", description: `${manifest.summary.by_category.dead_code} 个未使用文件` }
    ]
  },
  {
    id: "risk", type: "select",
    prompt: "风险级别?",
    options: [
      { label: "仅低风险", description: "最安全(推荐)" },
      { label: "低 + 中风险", description: "包括可能未使用的" },
      { label: "全部", description: "激进" }
    ]
  }
])  // 阻塞(等待用户响应)

阶段 4:执行

const riskFilter = {
  '仅低风险': ['low'],
  '低 + 中风险': ['low', 'medium'],
  '全部': ['low', 'medium', 'high']
}[selection.risk]

// 收集要清理的项目
const items = []
if (selection.categories.includes('会话')) {
  items.push(...manifest.discoveries.stale_sessions.filter(s => riskFilter.includes(s.risk)))
}
if (selection.categories.includes('文档')) {
  items.push(...manifest.discoveries.drifted_documents.filter(d => riskFilter.includes(d.risk)))
}
if (selection.categories.includes('死代码')) {
  items.push(...manifest.discoveries.dead_code.filter(c => riskFilter.includes(c.risk)))
}

const results = { staged: [], deleted: [], failed: [], skipped: [] }

// 验证并暂存
for (const item of items) {
  const validation = validatePath(item.path)
  if (!validation.valid) {
    results.skipped.push({ path: item.path, reason: validation.reason })
    continue
  }

  if (!fileExists(item.path) && !dirExists(item.path)) {
    results.skipped.push({ path: item.path, reason: '未找到' })
    continue
  }

  try {
    const trashTarget = `${trashFolder}/${item.path.replace(/\//g, '_')}`
    bash(`mv "${item.path}" "${trashTarget}"`)
    results.staged.push({ path: item.path, trashPath: trashTarget })
  } catch (e) {
    results.failed.push({ path: item.path, error: e.message })
  }
}

// 永久删除
for (const staged of results.staged) {
  try {
    bash(`rm -rf "${staged.trashPath}"`)
    results.deleted.push(staged.path)
  } catch (e) {
    console.error(`失败:${staged.path}`)
  }
}

// 清理空垃圾文件夹
bash(`rmdir "${trashFolder}" 2>/dev/null || true`)

// 报告
console.log(`
## 清理完成

**已删除**:${results.deleted.length}
**失败**:${results.failed.length}
**跳过**:${results.skipped.length}

### 已删除
${results.deleted.map(p => `- ${p}`).join('
') || '(无)'}

${results.failed.length > 0 ? `### 失败
${results.failed.map(f => `- ${f.path}: ${f.error}`).join('
')}` : ''}

报告:${sessionFolder}/cleanup-report.json
`)

Write(`${sessionFolder}/cleanup-report.json`, JSON.stringify({
  timestamp: getUtc8ISOString(),
  results,
  summary: {
    deleted: results.deleted.length,
    failed: results.failed.length,
    skipped: results.skipped.length
  }
}, null, 2))

会话文件夹

{projectRoot}/.workflow/.clean/clean-{YYYY-MM-DD}/
├── mainline-profile.json     # Git 历史分析
├── cleanup-manifest.json     # 发现结果
├── cleanup-report.json       # 执行结果
└── .trash/                   # 暂存区域(临时)

风险级别

风险 描述 示例
安全删除 空会话、草稿文件
可能未使用 孤立文件、旧存档
可能有依赖项 有导入的文件

安全功能

功能 保护
路径验证 白名单目录,拒绝遍历
暂存删除 在永久删除前移动到 .trash
危险模式 阻止系统目录、node_modules、.git

迭代流程

首次调用 (/prompts:clean):
   ├─ 从 git 历史检测主线
   ├─ 通过子代理发现陈旧工件
   ├─ 显示摘要,等待用户选择
   └─ 使用暂存执行清理

干运行 (/prompts:clean --dry-run):
   ├─ 除执行外的所有阶段
   └─ 保存清单供审查

定向 (/prompts:clean FOCUS="auth"):
   └─ 发现限于指定区域

错误处理

情况 操作
无 git 仓库 仅使用文件时间戳
代理超时 重试一次并提示,然后中止
路径验证失败 跳过项目,报告原因
清单解析错误 中止并报错
空发现 报告“代码库已清理”

现在执行清理工作流,焦点:$FOCUS