{"id":"353785b2-6239-44f5-ae8d-e308c9c5bf08","shortId":"eg4BLz","kind":"skill","title":"file-tracker","tagline":"Log all file changes (write, edit, delete) to a SQLite database for debugging and audit. Use when: (1) Tracking code changes, (2) Debugging issues, (3) Auditing file modifications, or (4) The user asks to track file changes.","description":"# File Tracker\n\nLog every file change (write, edit, delete) to a SQLite database for debugging, audit trails, and version history tracking. Works with any file operation system or code editor.\n\n## When to use\n\n- Tracking file modifications during development\n- Creating audit trails for file changes\n- Debugging what files were modified and when\n- Building version history without git\n- User asks to track or review file changes\n\n## Required tools / APIs\n\n- Python standard library (sqlite3, datetime, os)\n- Any programming language with SQLite support\n\nNo external APIs or services required.\n\n## Database Schema\n\n```sql\nCREATE TABLE IF NOT EXISTS file_changes (\n  id INTEGER PRIMARY KEY AUTOINCREMENT,\n  timestamp TEXT NOT NULL,\n  action TEXT NOT NULL,              -- 'write', 'edit', 'delete', 'rename'\n  file_path TEXT NOT NULL,\n  old_content TEXT,                  -- for edits/deletes\n  new_content TEXT,                  -- for writes/edits\n  file_size INTEGER,                 -- size in bytes\n  metadata TEXT,                     -- JSON: user, session_id, etc.\n  created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX idx_file_path ON file_changes(file_path);\nCREATE INDEX idx_timestamp ON file_changes(timestamp);\nCREATE INDEX idx_action ON file_changes(action);\n\n-- Automatic purge: delete records older than 1 year\nDELETE FROM file_changes WHERE created_at < datetime('now', '-1 year');\n```\n\n**Fields:**\n- `id` - Auto-incrementing primary key\n- `timestamp` - ISO 8601 timestamp of the change\n- `action` - Type of operation: 'write', 'edit', 'delete', 'rename'\n- `file_path` - Absolute or relative path to the file\n- `old_content` - Previous content (for edits) or deleted content (for deletes)\n- `new_content` - New content (for writes/edits)\n- `file_size` - File size in bytes after operation\n- `metadata` - JSON field for additional context (user, session, tools)\n- `created_at` - Database insertion timestamp\n\n## Basic Implementation\n\n### Python\n\n**Initialize database:**\n\n```python\nimport sqlite3\nfrom datetime import datetime\nfrom pathlib import Path\nimport json\nimport os\n\n# Configure database path (customize as needed)\nDB_PATH = Path.home() / \".file_tracker\" / \"changes.db\"\n\ndef init_db():\n    \"\"\"Initialize database and create tables.\"\"\"\n    DB_PATH.parent.mkdir(parents=True, exist_ok=True)\n    conn = sqlite3.connect(str(DB_PATH))\n    conn.execute(\"\"\"\n        CREATE TABLE IF NOT EXISTS file_changes (\n            id INTEGER PRIMARY KEY AUTOINCREMENT,\n            timestamp TEXT NOT NULL,\n            action TEXT NOT NULL,\n            file_path TEXT NOT NULL,\n            old_content TEXT,\n            new_content TEXT,\n            file_size INTEGER,\n            metadata TEXT,\n            created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n        )\n    \"\"\")\n    conn.execute(\"CREATE INDEX IF NOT EXISTS idx_file_path ON file_changes(file_path)\")\n    conn.execute(\"CREATE INDEX IF NOT EXISTS idx_timestamp ON file_changes(timestamp)\")\n    conn.execute(\"CREATE INDEX IF NOT EXISTS idx_action ON file_changes(action)\")\n    conn.commit()\n    conn.close()\n\ndef purge_old_changes():\n    \"\"\"Delete file change records older than 1 year to keep the database size sane.\"\"\"\n    conn = sqlite3.connect(str(DB_PATH))\n    conn.execute(\"DELETE FROM file_changes WHERE created_at < datetime('now', '-1 year')\")\n    conn.commit()\n    conn.close()\n\n# Initialize on import and purge old records\ninit_db()\npurge_old_changes()\n```\n\n**Log file changes:**\n\n```python\ndef log_file_change(\n    action: str,\n    file_path: str,\n    old_content: str = None,\n    new_content: str = None,\n    metadata: dict = None\n):\n    \"\"\"Log a file change to the database.\"\"\"\n    conn = sqlite3.connect(str(DB_PATH))\n    try:\n        # Get file size if file exists\n        file_size = None\n        if os.path.exists(file_path) and action != \"delete\":\n            file_size = os.path.getsize(file_path)\n\n        conn.execute(\n            \"\"\"INSERT INTO file_changes\n               (timestamp, action, file_path, old_content, new_content, file_size, metadata)\n               VALUES (?, ?, ?, ?, ?, ?, ?)\"\"\",\n            (\n                datetime.utcnow().isoformat(),\n                action,\n                file_path,\n                old_content[:5000] if old_content else None,  # Truncate large content\n                new_content[:5000] if new_content else None,\n                file_size,\n                json.dumps(metadata) if metadata else None\n            )\n        )\n        conn.commit()\n    finally:\n        conn.close()\n\n# Usage examples\nlog_file_change(\"write\", \"/path/to/file.py\", new_content=\"print('Hello')\")\nlog_file_change(\"edit\", \"/path/to/file.py\", old_content=\"print('Hello')\", new_content=\"print('Hi')\")\nlog_file_change(\"delete\", \"/path/to/file.py\", old_content=\"print('Hi')\")\nlog_file_change(\"write\", \"/path/to/config.json\", new_content='{\"key\": \"value\"}',\n                metadata={\"user\": \"john\", \"session\": \"sess_123\"})\n```\n\n**Tracked file operations:**\n\n```python\ndef tracked_write(file_path: str, content: str, metadata: dict = None):\n    \"\"\"Write file and log the change.\"\"\"\n    with open(file_path, 'w') as f:\n        f.write(content)\n    log_file_change(\"write\", file_path, new_content=content, metadata=metadata)\n\ndef tracked_edit(file_path: str, old_content: str, new_content: str, metadata: dict = None):\n    \"\"\"Edit file and log the change.\"\"\"\n    with open(file_path, 'w') as f:\n        f.write(new_content)\n    log_file_change(\"edit\", file_path, old_content=old_content,\n                    new_content=new_content, metadata=metadata)\n\ndef tracked_delete(file_path: str, metadata: dict = None):\n    \"\"\"Delete file and log the change.\"\"\"\n    with open(file_path, 'r') as f:\n        old_content = f.read()\n    os.remove(file_path)\n    log_file_change(\"delete\", file_path, old_content=old_content, metadata=metadata)\n\n# Usage\ntracked_write(\"example.txt\", \"Hello, World!\")\ntracked_edit(\"example.txt\", \"Hello, World!\", \"Hello, Python!\")\ntracked_delete(\"example.txt\")\n```\n\n**Query file history:**\n\n```python\ndef get_file_history(file_path: str, limit: int = 20):\n    \"\"\"Get change history for a specific file.\"\"\"\n    conn = sqlite3.connect(str(DB_PATH))\n    conn.row_factory = sqlite3.Row\n    cursor = conn.execute(\n        \"\"\"SELECT timestamp, action, old_content, new_content, file_size\n           FROM file_changes\n           WHERE file_path = ?\n           ORDER BY timestamp DESC\n           LIMIT ?\"\"\",\n        (file_path, limit)\n    )\n    results = cursor.fetchall()\n    conn.close()\n    return results\n\ndef get_recent_changes(limit: int = 50):\n    \"\"\"Get recent file changes across all files.\"\"\"\n    conn = sqlite3.connect(str(DB_PATH))\n    conn.row_factory = sqlite3.Row\n    cursor = conn.execute(\n        \"\"\"SELECT timestamp, action, file_path, file_size\n           FROM file_changes\n           ORDER BY timestamp DESC\n           LIMIT ?\"\"\",\n        (limit,)\n    )\n    results = cursor.fetchall()\n    conn.close()\n    return results\n\ndef search_file_changes(pattern: str):\n    \"\"\"Search for files matching a pattern.\"\"\"\n    conn = sqlite3.connect(str(DB_PATH))\n    conn.row_factory = sqlite3.Row\n    cursor = conn.execute(\n        \"\"\"SELECT timestamp, action, file_path\n           FROM file_changes\n           WHERE file_path LIKE ?\n           ORDER BY timestamp DESC\"\"\",\n        (f\"%{pattern}%\",)\n    )\n    results = cursor.fetchall()\n    conn.close()\n    return results\n\ndef get_changes_by_action(action: str, limit: int = 50):\n    \"\"\"Get all changes of a specific type (write, edit, delete).\"\"\"\n    conn = sqlite3.connect(str(DB_PATH))\n    conn.row_factory = sqlite3.Row\n    cursor = conn.execute(\n        \"\"\"SELECT timestamp, file_path, file_size\n           FROM file_changes\n           WHERE action = ?\n           ORDER BY timestamp DESC\n           LIMIT ?\"\"\",\n        (action, limit)\n    )\n    results = cursor.fetchall()\n    conn.close()\n    return results\n\n# Usage\nhistory = get_file_history(\"/path/to/file.py\")\nfor change in history:\n    print(f\"[{change['timestamp']}] {change['action']}: {change['file_size']} bytes\")\n\nrecent = get_recent_changes(10)\nprint(f\"Last {len(recent)} file changes\")\n\nedits = get_changes_by_action(\"edit\", limit=20)\nprint(f\"Found {len(edits)} file edits\")\n```\n\n### Node.js\n\n```javascript\nimport sqlite3 from \"sqlite3\";\nimport { promisify } from \"util\";\nimport path from \"path\";\nimport os from \"os\";\nimport fs from \"fs/promises\";\n\nconst DB_PATH = path.join(os.homedir(), \".file_tracker\", \"changes.db\");\n\n// Initialize database\nconst db = new sqlite3.Database(DB_PATH);\nconst run = promisify(db.run.bind(db));\nconst all = promisify(db.all.bind(db));\n\nawait run(`\n  CREATE TABLE IF NOT EXISTS file_changes (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    timestamp TEXT NOT NULL,\n    action TEXT NOT NULL,\n    file_path TEXT NOT NULL,\n    old_content TEXT,\n    new_content TEXT,\n    file_size INTEGER,\n    metadata TEXT,\n    created_at DATETIME DEFAULT CURRENT_TIMESTAMP\n  )\n`);\n\n// Log file change\nasync function logFileChange(action, filePath, oldContent = null, newContent = null, metadata = null) {\n  let fileSize = null;\n  try {\n    if (action !== \"delete\") {\n      const stats = await fs.stat(filePath);\n      fileSize = stats.size;\n    }\n  } catch (err) {\n    // File might not exist\n  }\n\n  await run(\n    `INSERT INTO file_changes (timestamp, action, file_path, old_content, new_content, file_size, metadata)\n     VALUES (?, ?, ?, ?, ?, ?, ?)`,\n    [\n      new Date().toISOString(),\n      action,\n      filePath,\n      oldContent,\n      newContent,\n      fileSize,\n      metadata ? JSON.stringify(metadata) : null,\n    ]\n  );\n}\n\n// Tracked file operations\nasync function trackedWrite(filePath, content, metadata = null) {\n  await fs.writeFile(filePath, content);\n  await logFileChange(\"write\", filePath, null, content, metadata);\n}\n\nasync function trackedEdit(filePath, oldContent, newContent, metadata = null) {\n  await fs.writeFile(filePath, newContent);\n  await logFileChange(\"edit\", filePath, oldContent, newContent, metadata);\n}\n\n// Query history\nasync function getFileHistory(filePath, limit = 20) {\n  return await all(\n    `SELECT timestamp, action, old_content, new_content, file_size\n     FROM file_changes\n     WHERE file_path = ?\n     ORDER BY timestamp DESC\n     LIMIT ?`,\n    [filePath, limit]\n  );\n}\n\n// Usage\nawait trackedWrite(\"example.txt\", \"Hello, World!\");\nconst history = await getFileHistory(\"example.txt\");\nconsole.log(history);\n```\n\n## Bash Quick Queries\n\n```bash\n# View recent file changes\nsqlite3 ~/.file_tracker/changes.db \"SELECT timestamp, action, file_path FROM file_changes ORDER BY timestamp DESC LIMIT 20\"\n\n# Get history for a specific file\nsqlite3 ~/.file_tracker/changes.db \"SELECT timestamp, action FROM file_changes WHERE file_path='/path/to/file' ORDER BY timestamp DESC\"\n\n# Count changes by action type\nsqlite3 ~/.file_tracker/changes.db \"SELECT action, COUNT(*) as count FROM file_changes GROUP BY action\"\n\n# Find all Python file changes\nsqlite3 ~/.file_tracker/changes.db \"SELECT timestamp, action, file_path FROM file_changes WHERE file_path LIKE '%.py' ORDER BY timestamp DESC\"\n\n# Export file history to JSON\nsqlite3 -json ~/.file_tracker/changes.db \"SELECT * FROM file_changes WHERE file_path='/path/to/file' ORDER BY timestamp ASC\" > file_history.json\n```\n\n## Integration Examples\n\n### Context Manager Pattern\n\n```python\nclass FileChangeTracker:\n    \"\"\"Context manager to automatically track file changes.\"\"\"\n\n    def __init__(self, file_path: str, action: str = \"edit\", metadata: dict = None):\n        self.file_path = file_path\n        self.action = action\n        self.metadata = metadata\n        self.old_content = None\n\n    def __enter__(self):\n        if os.path.exists(self.file_path):\n            with open(self.file_path, 'r') as f:\n                self.old_content = f.read()\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        if exc_type is None:  # No exception\n            new_content = None\n            if os.path.exists(self.file_path):\n                with open(self.file_path, 'r') as f:\n                    new_content = f.read()\n\n            log_file_change(\n                self.action,\n                self.file_path,\n                old_content=self.old_content,\n                new_content=new_content,\n                metadata=self.metadata\n            )\n\n# Usage\nwith FileChangeTracker(\"config.json\", action=\"edit\"):\n    # Modify file\n    with open(\"config.json\", 'w') as f:\n        f.write('{\"updated\": true}')\n# Change is automatically logged on exit\n```\n\n### Decorator Pattern\n\n```python\ndef track_file_operation(action: str):\n    \"\"\"Decorator to track file operations.\"\"\"\n    def decorator(func):\n        def wrapper(file_path, *args, **kwargs):\n            # Read old content if file exists\n            old_content = None\n            if os.path.exists(file_path) and action in [\"edit\", \"delete\"]:\n                with open(file_path, 'r') as f:\n                    old_content = f.read()\n\n            # Execute operation\n            result = func(file_path, *args, **kwargs)\n\n            # Read new content\n            new_content = None\n            if os.path.exists(file_path) and action in [\"write\", \"edit\"]:\n                with open(file_path, 'r') as f:\n                    new_content = f.read()\n\n            # Log change\n            log_file_change(action, file_path, old_content, new_content)\n\n            return result\n        return wrapper\n    return decorator\n\n# Usage\n@track_file_operation(\"write\")\ndef create_file(path, content):\n    with open(path, 'w') as f:\n        f.write(content)\n\n@track_file_operation(\"edit\")\ndef update_file(path, new_content):\n    with open(path, 'w') as f:\n        f.write(new_content)\n```\n\n## Agent Prompt\n\n```text\nYou have file change tracking capability. All file operations are logged to a SQLite database.\n\nWhen user asks to:\n- Review file change history\n- Track what files were modified\n- Find when a file was changed\n- Audit file operations\n\nUse the SQLite database at ~/.file_tracker/changes.db with this schema:\n- file_changes table (id, timestamp, action, file_path, old_content, new_content, file_size, metadata)\n\nAfter performing file operations (write, edit, delete), always log them:\n- Write: log_file_change(\"write\", file_path, new_content=content)\n- Edit: log_file_change(\"edit\", file_path, old_content=old, new_content=new)\n- Delete: log_file_change(\"delete\", file_path, old_content=content)\n\nQuery examples:\n1. File history: SELECT * FROM file_changes WHERE file_path = ? ORDER BY timestamp DESC\n2. Recent changes: SELECT * FROM file_changes ORDER BY timestamp DESC LIMIT 50\n3. Search files: SELECT * FROM file_changes WHERE file_path LIKE '%pattern%'\n\nAlways log file operations for audit trail and debugging purposes.\n```\n\n## Best Practices\n\n1. **Truncate large content** (e.g., 5000 chars) to avoid database bloat\n2. **Use indexes** on file_path, timestamp, and action for fast queries\n3. **Store full paths** for clarity and uniqueness\n4. **Log metadata** (user, session) for context\n5. **Regular cleanup** of old entries to manage database size\n6. **Privacy**: avoid storing sensitive file content\n7. **Compression**: consider compressing old_content/new_content for large text files\n\n## Troubleshooting\n\n**Database getting too large:**\n- Truncate old entries: `DELETE FROM file_changes WHERE timestamp < '2024-01-01'`\n- Run VACUUM: `sqlite3 changes.db \"VACUUM\"`\n- Limit content stored (already truncated to 5000 chars)\n\n**Missing file changes:**\n- Ensure log_file_change() is called after every file operation\n- Check file permissions for database writes\n- Verify DB_PATH is accessible\n\n**Query performance slow:**\n- Ensure indexes exist (file_path, timestamp, action)\n- Use LIMIT on queries\n- Consider archiving old entries\n\n## See also\n\n- [../chat-logger/SKILL.md](../chat-logger/SKILL.md) — Log chat messages\n- [../generate-report/SKILL.md](../generate-report/SKILL.md) — Generate HTML reports","tags":["file","tracker","open","skills","besoeasy","agent-skills","ai-agents","claude-code","clawdbot","clawdbot-skill","llm-tools","mcp-server"],"capabilities":["skill","source-besoeasy","skill-file-tracker","topic-agent-skills","topic-ai-agents","topic-claude-code","topic-clawdbot","topic-clawdbot-skill","topic-llm-tools","topic-mcp-server","topic-openai","topic-openclaw","topic-vibe-coding","topic-vibecoding"],"categories":["open-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/besoeasy/open-skills/file-tracker","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add besoeasy/open-skills","source_repo":"https://github.com/besoeasy/open-skills","install_from":"skills.sh"}},"qualityScore":"0.505","qualityRationale":"deterministic score 0.51 from registry signals: · indexed on github topic:agent-skills · 111 github stars · SKILL.md body (15,933 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-02T12:55:03.401Z","embedding":null,"createdAt":"2026-04-18T22:10:41.135Z","updatedAt":"2026-05-02T12:55:03.401Z","lastSeenAt":"2026-05-02T12:55:03.401Z","tsv":"'-01':1895,1896 '-1':230,469 '/.file_tracker/changes.db':1287,1309,1330,1348,1373,1700 '/chat-logger/skill.md':1954,1955 '/generate-report/skill.md':1959,1960 '/path/to/config.json':632 '/path/to/file':1319,1381 '/path/to/file.py':601,610,623,994 '1':21,219,446,1764,1815 '10':1013 '123':642 '2':25,1778,1826 '20':800,1028,1239,1301 '2024':1894 '3':28,1791,1838 '4':33,1846 '5':1853 '50':852,945,1790 '5000':567,578,1820,1908 '6':1863 '7':1870 '8601':241 'absolut':256 'access':1933 'across':857 'action':145,208,212,246,370,429,433,493,536,549,562,820,872,915,940,941,976,982,1004,1025,1102,1134,1147,1169,1183,1245,1290,1312,1327,1332,1341,1351,1408,1419,1497,1523,1553,1586,1605,1709,1834,1943 'addit':292 'agent':1655 'alreadi':1905 'also':1953 'alway':1726,1803 'api':107,122 'archiv':1949 'arg':1537,1573 'asc':1385 'ask':36,98,1675 'async':1131,1195,1213,1234 'audit':18,29,56,80,1692,1808 'auto':235 'auto-incr':234 'autoincr':140,365,1097 'automat':213,1398,1512 'avoid':1823,1865 'await':1084,1151,1162,1202,1206,1221,1225,1241,1266,1273 'bash':1278,1281 'basic':302 'best':1813 'bloat':1825 'build':92 'byte':173,285,1008 'call':1918 'capabl':1663 'catch':1156 'chang':7,24,40,46,84,104,135,194,203,211,224,245,360,407,420,432,439,442,463,484,487,492,512,547,599,608,621,630,663,675,704,717,745,761,802,829,849,856,879,894,920,938,948,974,996,1001,1003,1005,1012,1020,1023,1092,1130,1167,1254,1285,1295,1315,1325,1338,1346,1356,1377,1401,1479,1510,1601,1604,1661,1679,1691,1705,1732,1742,1755,1770,1780,1784,1797,1891,1912,1916 'changes.db':333,1065,1900 'char':1821,1909 'chat':1957 'check':1923 'clariti':1843 'class':1393 'cleanup':1855 'code':23,69 'compress':1871,1873 'config.json':1496,1503 'configur':322 'conn':348,454,516,808,860,903,956 'conn.close':435,472,594,843,888,933,986 'conn.commit':434,471,592 'conn.execute':353,396,410,422,459,543,817,869,912,965 'conn.row':813,865,908,961 'consid':1872,1948 'console.log':1276 'const':1058,1068,1074,1079,1149,1271 'content':159,164,264,266,271,275,277,380,383,499,503,553,555,566,570,575,577,581,603,612,616,625,634,653,672,680,681,691,694,714,722,724,726,728,754,766,768,822,824,1112,1115,1173,1175,1199,1205,1211,1247,1249,1423,1440,1461,1475,1484,1486,1488,1490,1541,1546,1565,1577,1579,1598,1609,1611,1627,1635,1645,1654,1713,1715,1737,1738,1747,1750,1760,1761,1818,1869,1903 'content/new_content':1875 'context':293,1389,1395,1852 'count':1324,1333,1335 'creat':79,129,181,187,197,205,226,297,340,354,390,397,411,423,465,1086,1122,1624 'current':185,394,1126 'cursor':816,868,911,964 'cursor.fetchall':842,887,932,985 'custom':325 'databas':14,53,126,299,306,323,338,451,515,1067,1672,1698,1824,1861,1881,1927 'date':1181 'datetim':112,183,228,311,313,392,467,1124 'datetime.utcnow':560 'db':328,336,351,457,481,519,811,863,906,959,1059,1069,1072,1078,1083,1930 'db.all.bind':1082 'db.run.bind':1077 'db_path.parent.mkdir':342 'debug':16,26,55,85,1811 'decor':1516,1525,1531,1617 'def':334,436,489,647,684,731,791,846,891,936,1402,1425,1444,1519,1530,1533,1623,1640 'default':184,393,1125 'delet':10,49,151,215,221,252,270,273,440,460,537,622,733,740,762,785,955,1148,1556,1725,1752,1756,1888 'desc':836,883,928,980,1261,1299,1323,1365,1777,1788 'develop':78 'dict':507,656,697,738,1412 'e.g':1819 'edit':9,48,150,251,268,609,686,699,718,778,954,1021,1026,1033,1035,1227,1410,1498,1555,1589,1639,1724,1739,1743 'editor':70 'edits/deletes':162 'els':571,582,590 'ensur':1913,1937 'enter':1426 'entri':1858,1887,1951 'err':1157 'etc':180 'everi':44,1920 'exampl':596,1388,1763 'example.txt':774,779,786,1268,1275 'exc':1447,1449,1451,1454 'except':1459 'execut':1567 'exist':133,345,358,401,415,427,527,1090,1161,1544,1939 'exit':1445,1515 'export':1366 'extern':121 'f':670,711,752,929,1000,1015,1030,1438,1473,1506,1563,1596,1633,1651 'f.read':755,1441,1476,1566,1599 'f.write':671,712,1507,1634,1652 'factori':814,866,909,962 'fast':1836 'field':232,290 'file':2,6,30,39,41,45,65,75,83,87,103,134,153,168,190,193,195,202,210,223,254,262,280,282,331,359,374,385,403,406,408,419,431,441,462,486,491,495,511,523,526,528,533,538,541,546,550,556,563,584,598,607,620,629,644,650,659,666,674,677,687,700,707,716,719,734,741,748,757,760,763,788,793,795,807,825,828,831,838,855,859,873,875,878,893,899,916,919,922,968,970,973,992,1006,1019,1034,1063,1091,1106,1117,1129,1158,1166,1170,1176,1193,1250,1253,1256,1284,1291,1294,1307,1314,1317,1337,1345,1352,1355,1358,1367,1376,1379,1400,1405,1416,1478,1500,1521,1528,1535,1543,1550,1559,1571,1583,1592,1603,1606,1620,1625,1637,1642,1660,1665,1678,1683,1689,1693,1704,1710,1716,1721,1731,1734,1741,1744,1754,1757,1765,1769,1772,1783,1793,1796,1799,1805,1830,1868,1879,1890,1911,1915,1921,1924,1940 'file-track':1 'file_history.json':1386 'filechangetrack':1394,1495 'filepath':1135,1153,1184,1198,1204,1209,1216,1223,1228,1237,1263 'files':1143,1154,1187 'final':593 'find':1342,1686 'found':1031 'fs':1055 'fs.stat':1152 'fs.writefile':1203,1222 'fs/promises':1057 'full':1840 'func':1532,1570 'function':1132,1196,1214,1235 'generat':1961 'get':522,792,801,847,853,937,946,991,1010,1022,1302,1882 'getfilehistori':1236,1274 'git':96 'group':1339 'hello':605,614,775,780,782,1269 'hi':618,627 'histori':60,94,789,794,803,990,993,998,1233,1272,1277,1303,1368,1680,1766 'html':1962 'id':136,179,233,361,1093,1707 'idx':189,199,207,402,416,428 'implement':303 'import':308,312,316,318,320,475,1038,1042,1046,1050,1054 'increment':236 'index':188,198,206,398,412,424,1828,1938 'init':335,480,1403 'initi':305,337,473,1066 'insert':300,544,1164 'int':799,851,944 'integ':137,170,362,387,1094,1119 'integr':1387 'iso':240 'isoformat':561 'issu':27 'javascript':1037 'john':639 'json':176,289,319,1370,1372 'json.dumps':586 'json.stringify':1189 'keep':449 'key':139,238,364,635,1096 'kwarg':1538,1574 'languag':116 'larg':574,1817,1877,1884 'last':1016 'len':1017,1032 'let':1142 'librari':110 'like':924,1360,1801 'limit':798,837,840,850,884,885,943,981,983,1027,1238,1262,1264,1300,1789,1902,1945 'log':4,43,485,490,509,597,606,619,628,661,673,702,715,743,759,1128,1477,1513,1600,1602,1668,1727,1730,1740,1753,1804,1847,1914,1956 'logfilechang':1133,1207,1226 'manag':1390,1396,1860 'match':900 'messag':1958 'metadata':174,288,388,506,558,587,589,637,655,682,683,696,729,730,737,769,770,1120,1140,1178,1188,1190,1200,1212,1219,1231,1411,1421,1491,1718,1848 'might':1159 'miss':1910 'modif':31,76 'modifi':89,1499,1685 'need':327 'new':163,274,276,382,502,554,576,580,602,615,633,679,693,713,725,727,823,1070,1114,1174,1180,1248,1460,1474,1487,1489,1576,1578,1597,1610,1644,1653,1714,1736,1749,1751 'newcont':1138,1186,1218,1224,1230 'node.js':1036 'none':501,505,508,530,572,583,591,657,698,739,1413,1424,1457,1462,1547,1580 'null':144,148,157,369,373,378,1101,1105,1110,1137,1139,1141,1144,1191,1201,1210,1220 'ok':346 'old':158,263,379,438,478,483,498,552,565,569,611,624,690,721,723,753,765,767,821,1111,1172,1246,1483,1540,1545,1564,1608,1712,1746,1748,1759,1857,1874,1886,1950 'oldcont':1136,1185,1217,1229 'older':217,444 'open':665,706,747,1433,1468,1502,1558,1591,1629,1647 'oper':66,249,287,645,1194,1522,1529,1568,1621,1638,1666,1694,1722,1806,1922 'order':833,880,925,977,1258,1296,1320,1362,1382,1774,1785 'os':113,321,1051,1053 'os.homedir':1062 'os.path.exists':532,1429,1464,1549,1582 'os.path.getsize':540 'os.remove':756 'parent':343 'path':154,191,196,255,259,317,324,329,352,375,404,409,458,496,520,534,542,551,564,651,667,678,688,708,720,735,749,758,764,796,812,832,839,864,874,907,917,923,960,969,1047,1049,1060,1073,1107,1171,1257,1292,1318,1353,1359,1380,1406,1415,1417,1431,1435,1466,1470,1482,1536,1551,1560,1572,1584,1593,1607,1626,1630,1643,1648,1711,1735,1745,1758,1773,1800,1831,1841,1931,1941 'path.home':330 'path.join':1061 'pathlib':315 'pattern':895,902,930,1391,1517,1802 'perform':1720,1935 'permiss':1925 'practic':1814 'previous':265 'primari':138,237,363,1095 'print':604,613,617,626,999,1014,1029 'privaci':1864 'program':115 'promisifi':1043,1076,1081 'prompt':1656 'purg':214,437,477,482 'purpos':1812 'py':1361 'python':108,304,307,488,646,783,790,1344,1392,1518 'queri':787,1232,1280,1762,1837,1934,1947 'quick':1279 'r':750,1436,1471,1561,1594 'read':1539,1575 'recent':848,854,1009,1011,1018,1283,1779 'record':216,443,479 'regular':1854 'relat':258 'renam':152,253 'report':1963 'requir':105,125 'result':841,845,886,890,931,935,984,988,1569,1613 'return':844,889,934,987,1240,1442,1612,1614,1616 'review':102,1677 'run':1075,1085,1163,1897 'sane':453 'schema':127,1703 'search':892,897,1792 'see':1952 'select':818,870,913,966,1243,1288,1310,1331,1349,1374,1767,1781,1794 'self':1404,1427,1443,1446 'self.action':1418,1480 'self.file':1414,1430,1434,1465,1469,1481 'self.metadata':1420,1492 'self.old':1422,1439,1485 'sensit':1867 'servic':124 'sess':641 'session':178,295,640,1850 'size':169,171,281,283,386,452,524,529,539,557,585,826,876,971,1007,1118,1177,1251,1717,1862 'skill' 'skill-file-tracker' 'slow':1936 'source-besoeasy' 'specif':806,951,1306 'sql':128 'sqlite':13,52,118,1671,1697 'sqlite3':111,309,1039,1041,1286,1308,1329,1347,1371,1899 'sqlite3.connect':349,455,517,809,861,904,957 'sqlite3.database':1071 'sqlite3.row':815,867,910,963 'standard':109 'stat':1150 'stats.size':1155 'store':1839,1866,1904 'str':350,456,494,497,500,504,518,652,654,689,692,695,736,797,810,862,896,905,942,958,1407,1409,1524 'support':119 'system':67 'tabl':130,341,355,1087,1706 'tb':1452 'text':142,146,155,160,165,175,367,371,376,381,384,389,1099,1103,1108,1113,1116,1121,1657,1878 'timestamp':141,186,200,204,239,242,301,366,395,417,421,548,819,835,871,882,914,927,967,979,1002,1098,1127,1168,1244,1260,1289,1298,1311,1322,1350,1364,1384,1708,1776,1787,1832,1893,1942 'toisostr':1182 'tool':106,296 'topic-agent-skills' 'topic-ai-agents' 'topic-claude-code' 'topic-clawdbot' 'topic-clawdbot-skill' 'topic-llm-tools' 'topic-mcp-server' 'topic-openai' 'topic-openclaw' 'topic-vibe-coding' 'topic-vibecoding' 'track':22,38,61,74,100,643,648,685,732,772,777,784,1192,1399,1520,1527,1619,1636,1662,1681 'trackededit':1215 'trackedwrit':1197,1267 'tracker':3,42,332,1064 'trail':57,81,1809 'tri':521,1145 'troubleshoot':1880 'true':344,347,1509 'truncat':573,1816,1885,1906 'type':247,952,1328,1448,1455 'uniqu':1845 'updat':1508,1641 'usag':595,771,989,1265,1493,1618 'use':19,73,1695,1827,1944 'user':35,97,177,294,638,1674,1849 'util':1045 'vacuum':1898,1901 'val':1450 'valu':559,636,1179 'verifi':1929 'version':59,93 'view':1282 'w':668,709,1504,1631,1649 'without':95 'work':62 'world':776,781,1270 'wrapper':1534,1615 'write':8,47,149,250,600,631,649,658,676,773,953,1208,1588,1622,1723,1729,1733,1928 'writes/edits':167,279 'year':220,231,447,470","prices":[{"id":"9c814991-7ed8-4ccb-bf8f-59131483b9e6","listingId":"353785b2-6239-44f5-ae8d-e308c9c5bf08","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"besoeasy","category":"open-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T22:10:41.135Z"}],"sources":[{"listingId":"353785b2-6239-44f5-ae8d-e308c9c5bf08","source":"github","sourceId":"besoeasy/open-skills/file-tracker","sourceUrl":"https://github.com/besoeasy/open-skills/tree/main/skills/file-tracker","isPrimary":false,"firstSeenAt":"2026-04-18T22:10:41.135Z","lastSeenAt":"2026-05-02T12:55:03.401Z"}],"details":{"listingId":"353785b2-6239-44f5-ae8d-e308c9c5bf08","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"besoeasy","slug":"file-tracker","github":{"repo":"besoeasy/open-skills","stars":111,"topics":["agent-skills","ai","ai-agents","claude-code","clawdbot","clawdbot-skill","llm-tools","mcp-server","openai","openclaw","vibe-coding","vibecoding"],"license":null,"html_url":"https://github.com/besoeasy/open-skills","pushed_at":"2026-03-31T13:05:30Z","description":"Battle-tested skill library for AI agents. Save 98% of API costs with ready-to-use code for crypto, PDFs, search, web scraping & more. No trial-and-error, no expensive APIs.","skill_md_sha":"9c390c98b6a75756272da2fb76cba7a5ca14c385","skill_md_path":"skills/file-tracker/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/besoeasy/open-skills/tree/main/skills/file-tracker"},"layout":"multi","source":"github","category":"open-skills","frontmatter":{"name":"file-tracker","description":"Log all file changes (write, edit, delete) to a SQLite database for debugging and audit. Use when: (1) Tracking code changes, (2) Debugging issues, (3) Auditing file modifications, or (4) The user asks to track file changes."},"skills_sh_url":"https://skills.sh/besoeasy/open-skills/file-tracker"},"updatedAt":"2026-05-02T12:55:03.401Z"}}