文件跟踪器Skill file-tracker

文件跟踪器技能用于将文件更改(写、编辑、删除)记录到SQLite数据库,支持调试、审计追踪和版本历史管理。适用于开发过程中跟踪代码更改、调试问题、审计文件修改或用户要求跟踪文件更改的场景。关键词:文件跟踪、SQLite、数据库、调试、审计、版本历史。

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

name: 文件跟踪器 description: “记录所有文件更改(写、编辑、删除)到SQLite数据库,用于调试和审计。使用场景:(1) 跟踪代码更改,(2) 调试问题,(3) 审计文件修改,或 (4) 用户要求跟踪文件更改。”

文件跟踪器

记录每个文件更改(写、编辑、删除)到SQLite数据库,用于调试、审计追踪和版本历史跟踪。适用于任何文件操作系统或代码编辑器。

使用时机

  • 开发过程中跟踪文件修改
  • 创建文件更改的审计追踪
  • 调试文件何时被修改
  • 在没有git的情况下构建版本历史
  • 用户要求跟踪或审查文件更改

所需工具 / API

  • Python标准库(sqlite3, datetime, os)
  • 任何支持SQLite的编程语言

无需外部API或服务。

数据库模式

CREATE TABLE IF NOT EXISTS file_changes (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  timestamp TEXT NOT NULL,
  action TEXT NOT NULL,              -- 'write', 'edit', 'delete', 'rename'
  file_path TEXT NOT NULL,
  old_content TEXT,                  -- 用于编辑/删除
  new_content TEXT,                  -- 用于写/编辑
  file_size INTEGER,                 -- 大小(字节)
  metadata TEXT,                     -- JSON: 用户, session_id, 等
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_file_path ON file_changes(file_path);
CREATE INDEX idx_timestamp ON file_changes(timestamp);
CREATE INDEX idx_action ON file_changes(action);

-- 自动清理:删除一年前的记录
DELETE FROM file_changes WHERE created_at < datetime('now', '-1 year');

字段:

  • id - 自动递增主键
  • timestamp - ISO 8601时间戳
  • action - 操作类型:‘write’, ‘edit’, ‘delete’, ‘rename’
  • file_path - 文件的绝对或相对路径
  • old_content - 先前内容(用于编辑)或删除内容(用于删除)
  • new_content - 新内容(用于写/编辑)
  • file_size - 操作后文件大小(字节)
  • metadata - JSON字段用于附加上下文(用户、会话、工具)
  • created_at - 数据库插入时间戳

基本实现

Python

初始化数据库:

import sqlite3
from datetime import datetime
from pathlib import Path
import json
import os

# 配置数据库路径(根据需要自定义)
DB_PATH = Path.home() / ".file_tracker" / "changes.db"

def init_db():
    """初始化数据库并创建表。"""
    DB_PATH.parent.mkdir(parents=True, exist_ok=True)
    conn = sqlite3.connect(str(DB_PATH))
    conn.execute("""
        CREATE TABLE IF NOT EXISTS file_changes (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp TEXT NOT NULL,
            action TEXT NOT NULL,
            file_path TEXT NOT NULL,
            old_content TEXT,
            new_content TEXT,
            file_size INTEGER,
            metadata TEXT,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )
    """)
    conn.execute("CREATE INDEX IF NOT EXISTS idx_file_path ON file_changes(file_path)")
    conn.execute("CREATE INDEX IF NOT EXISTS idx_timestamp ON file_changes(timestamp)")
    conn.execute("CREATE INDEX IF NOT EXISTS idx_action ON file_changes(action)")
    conn.commit()
    conn.close()

def purge_old_changes():
    """删除一年前的文件更改记录以保持数据库大小合理。"""
    conn = sqlite3.connect(str(DB_PATH))
    conn.execute("DELETE FROM file_changes WHERE created_at < datetime('now', '-1 year')")
    conn.commit()
    conn.close()

# 导入时初始化并清理旧记录
init_db()
purge_old_changes()

记录文件更改:

def log_file_change(
    action: str,
    file_path: str,
    old_content: str = None,
    new_content: str = None,
    metadata: dict = None
):
    """将文件更改记录到数据库。"""
    conn = sqlite3.connect(str(DB_PATH))
    try:
        # 获取文件大小,如果文件存在
        file_size = None
        if os.path.exists(file_path) and action != "delete":
            file_size = os.path.getsize(file_path)

        conn.execute(
            """INSERT INTO file_changes
               (timestamp, action, file_path, old_content, new_content, file_size, metadata)
               VALUES (?, ?, ?, ?, ?, ?, ?)""",
            (
                datetime.utcnow().isoformat(),
                action,
                file_path,
                old_content[:5000] if old_content else None,  # 截断大内容
                new_content[:5000] if new_content else None,
                file_size,
                json.dumps(metadata) if metadata else None
            )
        )
        conn.commit()
    finally:
        conn.close()

# 使用示例
log_file_change("write", "/path/to/file.py", new_content="print('Hello')")
log_file_change("edit", "/path/to/file.py", old_content="print('Hello')", new_content="print('Hi')")
log_file_change("delete", "/path/to/file.py", old_content="print('Hi')")
log_file_change("write", "/path/to/config.json", new_content='{"key": "value"}',
                metadata={"user": "john", "session": "sess_123"})

跟踪的文件操作:

def tracked_write(file_path: str, content: str, metadata: dict = None):
    """写文件并记录更改。"""
    with open(file_path, 'w') as f:
        f.write(content)
    log_file_change("write", file_path, new_content=content, metadata=metadata)

def tracked_edit(file_path: str, old_content: str, new_content: str, metadata: dict = None):
    """编辑文件并记录更改。"""
    with open(file_path, 'w') as f:
        f.write(new_content)
    log_file_change("edit", file_path, old_content=old_content,
                    new_content=new_content, metadata=metadata)

def tracked_delete(file_path: str, metadata: dict = None):
    """删除文件并记录更改。"""
    with open(file_path, 'r') as f:
        old_content = f.read()
    os.remove(file_path)
    log_file_change("delete", file_path, old_content=old_content, metadata=metadata)

# 使用
tracked_write("example.txt", "Hello, World!")
tracked_edit("example.txt", "Hello, World!", "Hello, Python!")
tracked_delete("example.txt")

查询文件历史:

def get_file_history(file_path: str, limit: int = 20):
    """获取特定文件的更改历史。"""
    conn = sqlite3.connect(str(DB_PATH))
    conn.row_factory = sqlite3.Row
    cursor = conn.execute(
        """SELECT timestamp, action, old_content, new_content, file_size
           FROM file_changes
           WHERE file_path = ?
           ORDER BY timestamp DESC
           LIMIT ?""",
        (file_path, limit)
    )
    results = cursor.fetchall()
    conn.close()
    return results

def get_recent_changes(limit: int = 50):
    """获取所有文件的最近文件更改。"""
    conn = sqlite3.connect(str(DB_PATH))
    conn.row_factory = sqlite3.Row
    cursor = conn.execute(
        """SELECT timestamp, action, file_path, file_size
           FROM file_changes
           ORDER BY timestamp DESC
           LIMIT ?""",
        (limit,)
    )
    results = cursor.fetchall()
    conn.close()
    return results

def search_file_changes(pattern: str):
    """搜索匹配模式的文件。"""
    conn = sqlite3.connect(str(DB_PATH))
    conn.row_factory = sqlite3.Row
    cursor = conn.execute(
        """SELECT timestamp, action, file_path
           FROM file_changes
           WHERE file_path LIKE ?
           ORDER BY timestamp DESC""",
        (f"%{pattern}%",)
    )
    results = cursor.fetchall()
    conn.close()
    return results

def get_changes_by_action(action: str, limit: int = 50):
    """获取特定类型的所有更改(写、编辑、删除)。"""
    conn = sqlite3.connect(str(DB_PATH))
    conn.row_factory = sqlite3.Row
    cursor = conn.execute(
        """SELECT timestamp, file_path, file_size
           FROM file_changes
           WHERE action = ?
           ORDER BY timestamp DESC
           LIMIT ?""",
        (action, limit)
    )
    results = cursor.fetchall()
    conn.close()
    return results

# 使用
history = get_file_history("/path/to/file.py")
for change in history:
    print(f"[{change['timestamp']}] {change['action']}: {change['file_size']} bytes")

recent = get_recent_changes(10)
print(f"Last {len(recent)} file changes")

edits = get_changes_by_action("edit", limit=20)
print(f"Found {len(edits)} file edits")

Node.js

import sqlite3 from "sqlite3";
import { promisify } from "util";
import path from "path";
import os from "os";
import fs from "fs/promises";

const DB_PATH = path.join(os.homedir(), ".file_tracker", "changes.db");

// 初始化数据库
const db = new sqlite3.Database(DB_PATH);
const run = promisify(db.run.bind(db));
const all = promisify(db.all.bind(db));

await run(`
  CREATE TABLE IF NOT EXISTS file_changes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    timestamp TEXT NOT NULL,
    action TEXT NOT NULL,
    file_path TEXT NOT NULL,
    old_content TEXT,
    new_content TEXT,
    file_size INTEGER,
    metadata TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  )
`);

// 记录文件更改
async function logFileChange(action, filePath, oldContent = null, newContent = null, metadata = null) {
  let fileSize = null;
  try {
    if (action !== "delete") {
      const stats = await fs.stat(filePath);
      fileSize = stats.size;
    }
  } catch (err) {
    // 文件可能不存在
  }

  await run(
    `INSERT INTO file_changes (timestamp, action, file_path, old_content, new_content, file_size, metadata)
     VALUES (?, ?, ?, ?, ?, ?, ?)`,
    [
      new Date().toISOString(),
      action,
      filePath,
      oldContent,
      newContent,
      fileSize,
      metadata ? JSON.stringify(metadata) : null,
    ]
  );
}

// 跟踪的文件操作
async function trackedWrite(filePath, content, metadata = null) {
  await fs.writeFile(filePath, content);
  await logFileChange("write", filePath, null, content, metadata);
}

async function trackedEdit(filePath, oldContent, newContent, metadata = null) {
  await fs.writeFile(filePath, newContent);
  await logFileChange("edit", filePath, oldContent, newContent, metadata);
}

// 查询历史
async function getFileHistory(filePath, limit = 20) {
  return await all(
    `SELECT timestamp, action, old_content, new_content, file_size
     FROM file_changes
     WHERE file_path = ?
     ORDER BY timestamp DESC
     LIMIT ?`,
    [filePath, limit]
  );
}

// 使用
await trackedWrite("example.txt", "Hello, World!");
const history = await getFileHistory("example.txt");
console.log(history);

Bash 快速查询

# 查看最近文件更改
sqlite3 ~/.file_tracker/changes.db "SELECT timestamp, action, file_path FROM file_changes ORDER BY timestamp DESC LIMIT 20"

# 获取特定文件的历史
sqlite3 ~/.file_tracker/changes.db "SELECT timestamp, action FROM file_changes WHERE file_path='/path/to/file' ORDER BY timestamp DESC"

# 按操作类型统计更改
sqlite3 ~/.file_tracker/changes.db "SELECT action, COUNT(*) as count FROM file_changes GROUP BY action"

# 查找所有Python文件更改
sqlite3 ~/.file_tracker/changes.db "SELECT timestamp, action, file_path FROM file_changes WHERE file_path LIKE '%.py' ORDER BY timestamp DESC"

# 导出文件历史到JSON
sqlite3 -json ~/.file_tracker/changes.db "SELECT * FROM file_changes WHERE file_path='/path/to/file' ORDER BY timestamp ASC" > file_history.json

集成示例

上下文管理器模式

class FileChangeTracker:
    """上下文管理器自动跟踪文件更改。"""

    def __init__(self, file_path: str, action: str = "edit", metadata: dict = None):
        self.file_path = file_path
        self.action = action
        self.metadata = metadata
        self.old_content = None

    def __enter__(self):
        if os.path.exists(self.file_path):
            with open(self.file_path, 'r') as f:
                self.old_content = f.read()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:  # 无异常
            new_content = None
            if os.path.exists(self.file_path):
                with open(self.file_path, 'r') as f:
                    new_content = f.read()

            log_file_change(
                self.action,
                self.file_path,
                old_content=self.old_content,
                new_content=new_content,
                metadata=self.metadata
            )

# 使用
with FileChangeTracker("config.json", action="edit"):
    # 修改文件
    with open("config.json", 'w') as f:
        f.write('{"updated": true}')
# 退出时自动记录更改

装饰器模式

def track_file_operation(action: str):
    """装饰器跟踪文件操作。"""
    def decorator(func):
        def wrapper(file_path, *args, **kwargs):
            # 如果文件存在,读取旧内容
            old_content = None
            if os.path.exists(file_path) and action in ["edit", "delete"]:
                with open(file_path, 'r') as f:
                    old_content = f.read()

            # 执行操作
            result = func(file_path, *args, **kwargs)

            # 读取新内容
            new_content = None
            if os.path.exists(file_path) and action in ["write", "edit"]:
                with open(file_path, 'r') as f:
                    new_content = f.read()

            # 记录更改
            log_file_change(action, file_path, old_content, new_content)

            return result
        return wrapper
    return decorator

# 使用
@track_file_operation("write")
def create_file(path, content):
    with open(path, 'w') as f:
        f.write(content)

@track_file_operation("edit")
def update_file(path, new_content):
    with open(path, 'w') as f:
        f.write(new_content)

代理提示

您具有文件更改跟踪能力。所有文件操作都记录到SQLite数据库。

当用户要求时:
- 审查文件更改历史
- 跟踪哪些文件被修改
- 查找文件何时被更改
- 审计文件操作

使用 ~/.file_tracker/changes.db 处的SQLite数据库,模式如下:
- file_changes 表 (id, timestamp, action, file_path, old_content, new_content, file_size, metadata)

执行文件操作(写、编辑、删除)后,总是记录它们:
- 写:log_file_change("write", file_path, new_content=content)
- 编辑:log_file_change("edit", file_path, old_content=old, new_content=new)
- 删除:log_file_change("delete", file_path, old_content=content)

查询示例:
1. 文件历史:SELECT * FROM file_changes WHERE file_path = ? ORDER BY timestamp DESC
2. 最近更改:SELECT * FROM file_changes ORDER BY timestamp DESC LIMIT 50
3. 搜索文件:SELECT * FROM file_changes WHERE file_path LIKE '%pattern%'

始终记录文件操作以进行审计追踪和调试。

最佳实践

  1. 截断大内容(例如,5000字符)以避免数据库膨胀
  2. 使用索引在file_path、timestamp和action上以快速查询
  3. 存储完整路径以清晰和唯一性
  4. 记录元数据(用户、会话)以获取上下文
  5. 定期清理旧条目以管理数据库大小
  6. 隐私:避免存储敏感文件内容
  7. 压缩:考虑压缩old_content/new_content以处理大型文本文件

故障排除

数据库变得太大:

  • 删除旧条目:DELETE FROM file_changes WHERE timestamp < '2024-01-01'
  • 运行VACUUM:sqlite3 changes.db "VACUUM"
  • 限制存储内容(已截断为5000字符)

缺少文件更改:

  • 确保log_file_change()在每个文件操作后被调用
  • 检查数据库写入的文件权限
  • 验证DB_PATH可访问

查询性能慢:

  • 确保索引存在(file_path, timestamp, action)
  • 在查询中使用LIMIT
  • 考虑归档旧条目

另请参阅