#!/usr/bin/env node /* * comment-sweep-scan.js * * Scan src/main, src/preload, src/renderer for "process-metadata" comment * prefixes — internal artifact references that leak into source (task/ticket * numbers, review filenames, dated fresh-eyes markers, team-lead section * pointers, in-house code-review IDs, round-N/shot-M etc.). Output one line * per hit as pipe-separated fields so downstream classification can be done * mechanically: * * family|path|lineno|match|context * * The classification (keep-stripped / delete / skip) is NOT decided here. * This is a listing tool; humans (or a second pass) split the buckets. * * Usage: * node tools/comment-sweep-scan.js # print tsv * node tools/comment-sweep-scan.js --json # print jsonl * node tools/comment-sweep-scan.js --stats # count by family * node tools/comment-sweep-scan.js --stats-file # count by family x file * * Scope: only .js files under src/main, src/preload, src/renderer. Tests are * intentionally skipped (test names carry ticket anchors used for reconciling * regression coverage — stripping them would break the audit trail). * * Patterns are anchored to comment territory: we require the match to sit * inside //, /* * / (block open/mid), or trailing after a code line preceded * by //. Strings that happen to contain "task #157" won't match. */ 'use strict'; const fs = require('fs'); const path = require('path'); const ROOT = path.resolve(__dirname, '..'); const SCAN_DIRS = ['src/main', 'src/preload', 'src/renderer']; const EXCLUDE_BASENAMES = new Set([]); // test files live under test/, already out of SCAN_DIRS // ----------------------------------------------------------------------------- // Pattern families // // Each family has a probe regex (matched against comment TEXT, case-insensitive // unless noted) plus a short "why". The scan does not decide keep/delete — // families exist so the sort/classify pass can group by shape. // ----------------------------------------------------------------------------- const FAMILIES = [ // Fresh-eyes P0 dated review reference. Shape: // Fresh-eyes P0 (2026-07-18, review-fresh-eyes.md #N ...): // Fresh-eyes (2026-07-18, review-fresh-eyes.md #N): { id: 'fresh-eyes-p0', re: /fresh[- ]eyes[^\n]*review[- ]fresh[- ]eyes\.md[^\n]*#\d+/i, why: 'F-11 dated review-file marker' }, { id: 'fresh-eyes-plain', re: /\bfresh[- ]eyes\s+p0\b/i, why: 'F-11 fresh-eyes P0 prefix (no review-file coord)' }, // Explicit review-file references (any review-*.md #N form). { id: 'review-md-ref', re: /review-[a-z0-9][\w-]*\.md\s*(?:#|item\s*)?\d*/i, why: 'F-11 review-*.md filename reference' }, // Ticket letters (Ticket A/B/C/D/G) and their numeric siblings. { id: 'ticket-letter', re: /\bticket\s+[A-Z]\b/, why: 'F-10 Ticket A/B/... letter code' }, { id: 'ticket-num', re: /\bticket\s*#\d+/i, why: 'F-10 Ticket #N number' }, // task #N / #NNN in comments (JS uses # only for private, so # in a comment // is almost always an issue ticket). { id: 'task-num', re: /\btask\s*#\d+/i, why: 'F-10 task #NNN' }, { id: 'bare-hash-num', re: /(? b[1] - a[1]); for (const [k, v] of rows) process.stdout.write(`${v.toString().padStart(4)} ${k}\n`); process.stdout.write(`${'-'.repeat(6)}\n${total.toString().padStart(4)} TOTAL hits\n`); process.stdout.write(`${files.length.toString().padStart(4)} files scanned\n`); } else if (mode === 'stats-file') { const by = new Map(); // path -> Map(family -> count) for (const h of hits) { if (!by.has(h.path)) by.set(h.path, new Map()); const inner = by.get(h.path); inner.set(h.family, (inner.get(h.family) || 0) + 1); } const rows = [...by.entries()].sort((a, b) => { const at = [...a[1].values()].reduce((x, y) => x + y, 0); const bt = [...b[1].values()].reduce((x, y) => x + y, 0); return bt - at; }); for (const [p, inner] of rows) { const total = [...inner.values()].reduce((x, y) => x + y, 0); process.stdout.write(`${total.toString().padStart(4)} ${p}\n`); const fams = [...inner.entries()].sort((a, b) => b[1] - a[1]); for (const [f, c] of fams) process.stdout.write(` ${c.toString().padStart(3)} ${f}\n`); } } } if (require.main === module) main(); module.exports = { extractComments, FAMILIES, scanFile, listJsFiles };