返回顶部
o

openclaw-cleanerOpenClaw清理大师

OpenClaw清理大师 - 项目目录清理与优化工具。提供快照、Diff、检查点、任务进度等能力,AI 可直接调用自动执行。

作者: admin | 来源: ClawHub
源自
ClawHub
版本
V 1.0.0
安全检测
已通过
265
下载量
免费
免费
0
收藏
概述
安装方式
版本历史

openclaw-cleaner

🧹 OpenClaw Cleaner (清理大师)

AI 可直接调用的项目清理与优化工具

本技能提供一系列自动化能力,AI 可在执行任务时直接调用,无需用户确认。



能力总览


能力说明何时调用
快照记录文件状态执行修改前
Diff
对比变更差异 | 修改完成后展示给用户 |
| Checkpoint | 保存关键状态 | 重要操作前 |
| Task Progress | 追踪长时间任务 | 批量处理时 |
| Health Check | 评估目录健康度 | 用户询问或定期 |
| Skill Match | 推荐相关 Skills | 用户描述任务时 |
| Optimize | 优化文件减少 Token | 空间不足或定期 |


使用示例

场景:删除一个 Skill

javascript
// 1. 先创建快照(自动,也可手动)
const snap = await snapshot.create(before-remove-old-skill);

// 2. 执行删除
await skills.remove(unused-skill);

// 3. 对比变更,展示给用户
const diff = await snapshot.compare(snap.name, latest);
console.log(snapshot.generateReport(diff));
// 用户看到: 我删除了 xxx,新增了 yyy

场景:批量处理文件

javascript
// 1. 开始任务
const taskInfo = await task.start(process-files, fileList);

// 2. 处理每个文件
for (const file of files) {
try {
await processFile(file);
await task.markCompleted(taskInfo.id, index);
} catch (e) {
await task.markFailed(taskInfo.id, index, e.message);
}
}

// 3. 用户可随时查看进度
const status = await task.getStatus(taskInfo.id);



返回格式

javascript
// snapshot.compare()
{
added: [{ path: string, size: number, content?: string }],
removed: [{ path: string, size: number }],
modified: [{ path: string, oldSize: number, newSize: number, changes: [] }]
}

// health.check()
{
score: number, // 0-100
totalFiles: number,
totalDirs: number,
totalSize: number,
warnings: string[]
}



版本

v2.1.2 - 整合为单文件版本




javascript
// ============================================================================
// OpenClaw Cleaner - 完整实现代码 (2.1.2)
// ============================================================================

import fs from fs/promises;
import path from path;
import crypto from crypto;

// 获取工作空间路径
function getWorkspacePath() { return process.cwd(); }

// 静默日志
const silentLogger = { info: () => {}, debug: () => {}, warn: () => {}, error: () => {}, success: () => {}, section: () => {} };

// ============================================================================
// VisualDiffService - 快照和 Diff 服务
// ============================================================================
class VisualDiffService {
constructor(workspacePath, options = {}) {
this.workspacePath = workspacePath;
this.backupDir = options.backupDir || .cleaner-backups;
this.logger = options.logger;
}
getBackupPath(name = default) { return path.join(this.workspacePath, this.backupDir, name); }

async scanDirectory(dirPath, basePath = dirPath) {
const files = {};
async function walk(dir) {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
const relativePath = path.relative(basePath, fullPath);
if (entry.isDirectory()) {
if ([node_modules, .git, .cleaner-backups, pycache].includes(entry.name)) continue;
await walk(fullPath);
} else if (entry.isFile()) {
try {
const stat = await fs.stat(fullPath);
const content = await fs.readFile(fullPath, utf-8);
const hash = crypto.createHash(md5).update(content).digest(hex);
files[relativePath] = { path: relativePath, size: stat.size, modified: stat.mtime.toISOString(), hash, content };
} catch {}
}
}
}
await walk(dirPath);
return files;
}

async createSnapshot(name = manual) {
const timestamp = new Date().toISOString().replace(/[:.]/g, -);
const snapshotName = snapshot${name}${timestamp};
const snapshotPath = path.join(this.getBackupPath(), snapshots, snapshotName);
await fs.mkdir(snapshotPath, { recursive: true });
const files = await this.scanDirectory(this.workspacePath, this.workspacePath);
await fs.writeFile(path.join(snapshotPath, files.json), JSON.stringify(files, null, 2));
await fs.writeFile(path.join(snapshotPath, .meta.json), JSON.stringify({ name: snapshotName, createdAt: new Date().toISOString(), fileCount: Object.keys(files).length }, null, 2));
return { name: snapshotName, path: snapshotPath, fileCount: Object.keys(files).length };
}

async compare(snapshotA, snapshotB) {
const pathA = snapshotA.includes(/) ? snapshotA : path.join(this.getBackupPath(), snapshots, snapshotA);
const pathB = snapshotB.includes(/) ? snapshotB : path.join(this.getBackupPath(), snapshots, snapshotB);
let filesA, filesB;
try { filesA = JSON.parse(await fs.readFile(path.join(pathA, files.json), utf-8)); } catch { throw new Error(Snapshot A not found: ${snapshotA}); }
try { filesB = JSON.parse(await fs.readFile(path.join(pathB, files.json), utf-8)); } catch { throw new Error(Snapshot B not found: ${snapshotB}); }
const diff = { added: [], removed: [], modified: [], unchanged: [] };
const pathsA = new Set(Object.keys(filesA)), pathsB = new Set(Object.keys(filesB));
for (const filePath of pathsB) { if (!pathsA.has(filePath)) diff.added.push({ path: filePath, size: filesB[filePath].size, content: filesB[filePath].content }); }
for (const filePath of pathsA) { if (!pathsB.has(filePath)) diff.removed.push({ path: filePath, size: filesA[filePath].size, content: filesA[filePath].content }); }
for (const filePath of pathsA) { if (pathsB.has(filePath) && filesA[filePath].hash !== filesB[filePath].hash) diff.modified.push({ path: filePath, oldSize: filesA[filePath].size, newSize: filesB[filePath].size, oldContent: filesA[filePath].content, newContent: filesB[filePath].content }); }
return diff;
}

generateReport(diff) {
const lines = [];
lines.push(═.repeat(60)); lines.push(📊 可视化 Diff 报告); lines.push(═.repeat(60));
lines.push(\n📈 统计: 🟢 新增 ${diff.added.length} | 🔴 删除 ${diff.removed.length} | 🟡 修改 ${diff.modified.length}\n);
if (diff.added.length > 0) { lines.push(🟢 新增文件:); for (const f of diff.added) lines.push( + ${f.path} (${f.size}B)); lines.push(); }
if (diff.removed.length > 0) { lines.push(🔴 删除文件:); for (const f of diff.removed) lines.push( - ${f.path} (${f.size}B)); lines.push(); }
if (diff.modified.length > 0) { lines.push(🟡 修改文件:); for (const f of diff.modified) lines.push( ~ ${f.path} (${f.oldSize}B → ${f.newSize}B)); lines.push(); }
lines.push(═.repeat(60));
return lines.join(\n);
}

async listSnapshots() {
const snapshotsPath = path.join(this.getBackupPath(), snapshots);
try {
const entries = await fs.readdir(snapshotsPath, { withFileTypes: true });
const snapshots = [];
for (const entry of entries) {
if (entry.isDirectory()) {
try { const meta = JSON.parse(await fs.readFile(path.join

标签

skill ai

通过对话安装

该技能支持在以下平台通过对话安装:

OpenClaw WorkBuddy QClaw Kimi Claude

方式一:安装 SkillHub 和技能

帮我安装 SkillHub 和 openclaw-cleaner-1776174142 技能

方式二:设置 SkillHub 为优先技能安装源

设置 SkillHub 为我的优先技能安装源,然后帮我安装 openclaw-cleaner-1776174142 技能

通过命令行安装

skillhub install openclaw-cleaner-1776174142

下载

⬇ 下载 openclaw-cleaner v1.0.0(免费)

文件大小: 7.1 KB | 发布时间: 2026-4-15 13:03

v1.0.0 最新 2026-4-15 13:03
A cleaning software toolbox for Openclaw

Archiver·手机版·闲社网·闲社论坛·羊毛社区· 多链控股集团有限公司 · 苏ICP备2025199260号-1

Powered by Discuz! X5.0   © 2024-2025 闲社网·线报更新论坛·羊毛分享社区·http://xianshe.com

p2p_official_large
返回顶部