residoo 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -317,7 +317,7 @@ As a GitHub Action (this repository doubles as a composite action):
317
317
  ```yaml
318
318
  steps:
319
319
  - uses: actions/checkout@v4
320
- - uses: dandovdub/residoo@v0.3.6
320
+ - uses: dandovdub/residoo@v0.3.7
321
321
  ```
322
322
 
323
323
  As a pre-commit hook:
@@ -325,7 +325,7 @@ As a pre-commit hook:
325
325
  ```yaml
326
326
  repos:
327
327
  - repo: https://github.com/dandovdub/residoo
328
- rev: v0.3.6
328
+ rev: v0.3.7
329
329
  hooks:
330
330
  - id: residoo
331
331
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "residoo",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Find secrets leaking through your AI coding agent's session history. Zero network calls in the scan path, zero dependencies.",
5
5
  "license": "MIT",
6
6
  "author": "CloudRoam (https://cloudroam.io)",
package/src/report.js CHANGED
@@ -197,7 +197,15 @@ function render({ findings, filesScanned, sourcesScanned, bytesScanned, suppress
197
197
  byRule.get(f.ruleId).items.push(f);
198
198
  }
199
199
  const byFile = new Map();
200
- for (const f of findings) byFile.set(f.file, (byFile.get(f.file) || 0) + 1);
200
+ for (const f of findings) {
201
+ const entry = byFile.get(f.file) || { count: 0, mtimeMs: f.fileMTimeMs };
202
+ entry.count++;
203
+ // Newest mtime wins if a file's own findings ever carried different
204
+ // values (they should not, mtimeMs is a per-file stat, but a defensive
205
+ // max here costs nothing and avoids depending on finding order).
206
+ if (f.fileMTimeMs > entry.mtimeMs) entry.mtimeMs = f.fileMTimeMs;
207
+ byFile.set(f.file, entry);
208
+ }
201
209
  const oldest = findings.reduce((a, b) => (b.fileMTimeMs < a ? b.fileMTimeMs : a), Date.now());
202
210
  const newest = findings.reduce((a, b) => (b.fileMTimeMs > a ? b.fileMTimeMs : a), 0);
203
211
 
@@ -230,9 +238,9 @@ function render({ findings, filesScanned, sourcesScanned, bytesScanned, suppress
230
238
 
231
239
  push();
232
240
  push(paint(c.bold, "By file:"));
233
- const fileRows = [...byFile.entries()].sort((a, b) => b[1] - a[1]).slice(0, 15);
234
- for (const [file, count] of fileRows) {
235
- push(` ${String(count).padStart(4)} ${paint(c.cyan, safeBasename(file))}`);
241
+ const fileRows = [...byFile.entries()].sort((a, b) => b[1].count - a[1].count).slice(0, 15);
242
+ for (const [file, { count, mtimeMs }] of fileRows) {
243
+ push(` ${String(count).padStart(4)} ${paint(c.dim, `~${String(ageDays(mtimeMs)).padStart(2)}d old`)} ${paint(c.cyan, safeBasename(file))}`);
236
244
  }
237
245
  if (byFile.size > fileRows.length) push(paint(c.dim, ` … and ${byFile.size - fileRows.length} more file(s)`));
238
246
 
@@ -277,6 +285,7 @@ function renderJson(result, integrity = null, rotation = null) {
277
285
  findings: result.findings.map((f) => ({
278
286
  rule: f.ruleId, label: f.label, confidence: f.confidence,
279
287
  source: f.source, file: f.relFile, line: f.line, preview: f.preview,
288
+ fileMTimeMs: f.fileMTimeMs,
280
289
  // Markers for the two decode/reconstruct passes (absent on ordinary
281
290
  // findings). `encoding` names how the value was wrapped ("base64" /
282
291
  // "base64url"); `spanLines` names the adjacent line pair a split value