@wrongstack/plugins 0.295.0 → 0.296.2

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.
Files changed (110) hide show
  1. package/dist/accessibility-auditor/index.d.ts.map +1 -1
  2. package/dist/accessibility-auditor.js +31 -3
  3. package/dist/agent-handoff/index.d.ts.map +1 -1
  4. package/dist/agent-handoff.js +30 -1
  5. package/dist/auto-doc/index.d.ts.map +1 -1
  6. package/dist/auto-doc.js +3 -1
  7. package/dist/auto-i18n-extractor.js +5 -0
  8. package/dist/branch-guard/index.d.ts.map +1 -1
  9. package/dist/branch-guard.js +116 -3
  10. package/dist/changelog-writer/index.d.ts.map +1 -1
  11. package/dist/changelog-writer.js +137 -3
  12. package/dist/checkpoint/index.d.ts +2 -0
  13. package/dist/checkpoint/index.d.ts.map +1 -1
  14. package/dist/checkpoint.js +34 -8
  15. package/dist/code-metrics.js +5 -0
  16. package/dist/commit-validator/index.d.ts.map +1 -1
  17. package/dist/commit-validator.js +35 -8
  18. package/dist/context-pins/index.d.ts.map +1 -1
  19. package/dist/context-pins.js +6 -5
  20. package/dist/cost-tracker/index.d.ts.map +1 -1
  21. package/dist/cost-tracker.js +101 -1
  22. package/dist/cron/index.d.ts.map +1 -1
  23. package/dist/cron.js +1 -0
  24. package/dist/dead-code-detector/index.d.ts.map +1 -1
  25. package/dist/dead-code-detector.js +9 -1
  26. package/dist/dependency-vulnerability-gate/index.d.ts.map +1 -1
  27. package/dist/dependency-vulnerability-gate.js +43 -8
  28. package/dist/diff-summary/index.d.ts.map +1 -1
  29. package/dist/diff-summary.js +112 -2
  30. package/dist/doc-sync-guard.js +17 -1
  31. package/dist/duplicate-code-detector/index.d.ts.map +1 -1
  32. package/dist/duplicate-code-detector.js +102 -2
  33. package/dist/feature-flag-tracker/index.d.ts.map +1 -1
  34. package/dist/feature-flag-tracker.js +30 -4
  35. package/dist/file-watcher/index.d.ts.map +1 -1
  36. package/dist/file-watcher.js +6 -7
  37. package/dist/format-on-save/index.d.ts +2 -0
  38. package/dist/format-on-save/index.d.ts.map +1 -1
  39. package/dist/format-on-save.js +288 -59
  40. package/dist/git-autocommit/index.d.ts +12 -0
  41. package/dist/git-autocommit/index.d.ts.map +1 -1
  42. package/dist/git-autocommit.js +58 -4
  43. package/dist/import-organizer/index.d.ts.map +1 -1
  44. package/dist/import-organizer.js +152 -21
  45. package/dist/index.js +1413 -590
  46. package/dist/injection-shield/index.d.ts +16 -0
  47. package/dist/injection-shield/index.d.ts.map +1 -1
  48. package/dist/injection-shield.js +12 -4
  49. package/dist/interface-contract-guard/index.d.ts.map +1 -1
  50. package/dist/interface-contract-guard.js +60 -26
  51. package/dist/knowledge-graph/index.d.ts.map +1 -1
  52. package/dist/knowledge-graph.js +7 -6
  53. package/dist/lint-gate/index.d.ts.map +1 -1
  54. package/dist/lint-gate.js +115 -2
  55. package/dist/loop-breaker/index.d.ts.map +1 -1
  56. package/dist/loop-breaker.js +15 -5
  57. package/dist/migration-planner.js +15 -1
  58. package/dist/notify-hub/index.d.ts.map +1 -1
  59. package/dist/notify-hub.js +41 -4
  60. package/dist/pr-drafter/index.d.ts.map +1 -1
  61. package/dist/pr-drafter.js +20 -1
  62. package/dist/prompt-firewall/index.d.ts.map +1 -1
  63. package/dist/prompt-firewall.js +140 -11
  64. package/dist/refactor-suggester/index.d.ts.map +1 -1
  65. package/dist/refactor-suggester.js +119 -4
  66. package/dist/runtime/bounded-map.d.ts +86 -0
  67. package/dist/runtime/bounded-map.d.ts.map +1 -0
  68. package/dist/runtime/credential-patterns.d.ts +42 -0
  69. package/dist/runtime/credential-patterns.d.ts.map +1 -0
  70. package/dist/runtime/handles.d.ts +46 -0
  71. package/dist/runtime/handles.d.ts.map +1 -0
  72. package/dist/runtime/index.d.ts +4 -0
  73. package/dist/runtime/index.d.ts.map +1 -1
  74. package/dist/runtime/local-bin.d.ts +120 -0
  75. package/dist/runtime/local-bin.d.ts.map +1 -0
  76. package/dist/runtime/safe-json.d.ts +25 -0
  77. package/dist/runtime/safe-json.d.ts.map +1 -0
  78. package/dist/runtime.js +309 -22
  79. package/dist/schema-evolution-guard.js +5 -0
  80. package/dist/secret-scanner/index.d.ts.map +1 -1
  81. package/dist/secret-scanner.js +194 -50
  82. package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
  83. package/dist/security-hotspot-scanner.js +132 -1
  84. package/dist/semantic-search-indexer/index.d.ts.map +1 -1
  85. package/dist/semantic-search-indexer.js +7 -1
  86. package/dist/semver-bump/index.d.ts.map +1 -1
  87. package/dist/semver-bump.js +21 -2
  88. package/dist/session-recap/index.d.ts.map +1 -1
  89. package/dist/session-recap.js +18 -1
  90. package/dist/smart-rename/index.d.ts +2 -0
  91. package/dist/smart-rename/index.d.ts.map +1 -1
  92. package/dist/smart-rename.js +16 -2
  93. package/dist/spec-linker/index.d.ts.map +1 -1
  94. package/dist/spec-linker.js +19 -2
  95. package/dist/template-engine/index.d.ts.map +1 -1
  96. package/dist/template-engine.js +45 -3
  97. package/dist/test-coverage-gate.js +17 -1
  98. package/dist/test-runner-gate/index.d.ts.map +1 -1
  99. package/dist/test-runner-gate.js +142 -9
  100. package/dist/todo-listener/index.d.ts.map +1 -1
  101. package/dist/todo-listener.js +42 -2
  102. package/dist/todo-tracker/index.d.ts.map +1 -1
  103. package/dist/todo-tracker.js +3 -4
  104. package/dist/token-budget/index.d.ts.map +1 -1
  105. package/dist/token-budget.js +31 -19
  106. package/dist/token-throttle/index.d.ts.map +1 -1
  107. package/dist/token-throttle.js +17 -2
  108. package/dist/type-gate/index.d.ts.map +1 -1
  109. package/dist/type-gate.js +108 -17
  110. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/accessibility-auditor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AASrD,MAAM,MAAM,QAAQ,GAChB,aAAa,GACb,qBAAqB,GACrB,0BAA0B,GAC1B,qBAAqB,GACrB,cAAc,CAAC;AAEnB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB;AAuPD,QAAA,MAAM,MAAM,EAAE,MA0Nb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/accessibility-auditor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AASrD,MAAM,MAAM,QAAQ,GAChB,aAAa,GACb,qBAAqB,GACrB,0BAA0B,GAC1B,qBAAqB,GACrB,cAAc,CAAC;AAEnB,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB;AAwQD,QAAA,MAAM,MAAM,EAAE,MA8Nb,CAAC;eAEa,MAAM"}
@@ -4,6 +4,22 @@ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 }
4
4
 
5
5
  // src/runtime/index.ts
6
6
  import { basename, extname, isAbsolute, relative, resolve } from "node:path";
7
+
8
+ // src/runtime/local-bin.ts
9
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
10
+
11
+ // src/runtime/handles.ts
12
+ function releaseHandle(off) {
13
+ if (off) {
14
+ try {
15
+ off();
16
+ } catch {
17
+ }
18
+ }
19
+ return null;
20
+ }
21
+
22
+ // src/runtime/index.ts
7
23
  var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
8
24
  function hasLeadingDash(arg) {
9
25
  return arg.length > 0 && arg.startsWith("-");
@@ -204,15 +220,23 @@ async function auditPath(rawPath, cfg) {
204
220
  const exts = normalizeExtensions(cfg.includeExtensions);
205
221
  const files = await collectSourceFilesAsync(resolved, { extensions: exts });
206
222
  const findings = [];
223
+ let scannedFiles = 0;
224
+ let truncated = false;
207
225
  for (const file of files) {
208
226
  const fileFindings = await auditFile(file, root);
227
+ scannedFiles += 1;
209
228
  findings.push(...fileFindings);
210
- if (findings.length >= cfg.maxFindings) break;
229
+ if (findings.length >= cfg.maxFindings) {
230
+ truncated = scannedFiles < files.length;
231
+ break;
232
+ }
211
233
  }
212
234
  return {
213
235
  path: relative2(root, resolved),
214
236
  findings: findings.slice(0, cfg.maxFindings),
215
- fileCount: files.length
237
+ fileCount: files.length,
238
+ scannedFiles,
239
+ truncated
216
240
  };
217
241
  }
218
242
  function formatSummary(result) {
@@ -272,7 +296,7 @@ var plugin = {
272
296
  state.findingCount = 0;
273
297
  state.hookInvocationCount = 0;
274
298
  state.lastResult = null;
275
- state.hookUnregister = null;
299
+ state.hookUnregister = releaseHandle(state.hookUnregister);
276
300
  const cfg = readConfig(api.config.extensions?.["accessibility-auditor"]);
277
301
  const hook = async (input) => {
278
302
  if (!cfg.enabled || !cfg.onWriteEdit) return;
@@ -341,6 +365,10 @@ var plugin = {
341
365
  ok: true,
342
366
  path: result.path,
343
367
  fileCount: result.fileCount,
368
+ scannedFiles: result.scannedFiles,
369
+ // Say so when the cap stopped the walk early: a partial scan
370
+ // that reports few findings must not read as a clean result.
371
+ truncated: result.truncated,
344
372
  findingCount: result.findings.length,
345
373
  findings: result.findings
346
374
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent-handoff/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAwKrD,QAAA,MAAM,MAAM,EAAE,MAsNb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent-handoff/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA2KrD,QAAA,MAAM,MAAM,EAAE,MAsNb,CAAC;eAEa,MAAM"}
@@ -1,3 +1,32 @@
1
+ // src/runtime/local-bin.ts
2
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
3
+
4
+ // src/runtime/safe-json.ts
5
+ var UNSERIALIZABLE = "[unserializable]";
6
+ function safeJsonStringify(value, indent) {
7
+ try {
8
+ const stack = [];
9
+ const out = JSON.stringify(
10
+ value,
11
+ function replacer(_key, val) {
12
+ if (typeof val === "bigint") return `${val.toString()}n`;
13
+ if (val === null || typeof val !== "object") return val;
14
+ while (stack.length > 0 && stack[stack.length - 1] !== this) stack.pop();
15
+ if (stack.includes(val)) return "[circular]";
16
+ stack.push(val);
17
+ return val;
18
+ },
19
+ indent
20
+ );
21
+ return out ?? String(value);
22
+ } catch {
23
+ return UNSERIALIZABLE;
24
+ }
25
+ }
26
+
27
+ // src/runtime/index.ts
28
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
29
+
1
30
  // src/agent-handoff/index.ts
2
31
  var API_VERSION = "^0.1.10";
3
32
  var state = {
@@ -55,7 +84,7 @@ function buildBody(payload, cfg) {
55
84
  if (cfg.includeResult && payload.result !== void 0) {
56
85
  lines.push("## Result");
57
86
  lines.push("```json");
58
- lines.push(JSON.stringify(payload.result, null, 2));
87
+ lines.push(safeJsonStringify(payload.result, 2));
59
88
  lines.push("```");
60
89
  lines.push("");
61
90
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auto-doc/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,MAAM,EAAa,MAAM,wBAAwB,CAAC;AA4WhE,QAAA,MAAM,MAAM,EAAE,MAqIb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auto-doc/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,MAAM,EAAa,MAAM,wBAAwB,CAAC;AAsXhE,QAAA,MAAM,MAAM,EAAE,MAqIb,CAAC;eAEa,MAAM"}
package/dist/auto-doc.js CHANGED
@@ -196,6 +196,7 @@ async function runAutoDoc(input, api) {
196
196
  }
197
197
  const entities = parseSource(content);
198
198
  let modified = content;
199
+ const beforeCount = results.length;
199
200
  for (const entity of entities) {
200
201
  if (!input.force && !needsDocComment(modified, entity)) continue;
201
202
  let doc = null;
@@ -221,7 +222,8 @@ async function runAutoDoc(input, api) {
221
222
  modified = injectDocComment(modified, entity, doc);
222
223
  results.push({ file: safeFile, entity: entity.name, source });
223
224
  }
224
- if (!input.dry_run && results.length > 0) {
225
+ const changedThisFile = results.length > beforeCount && modified !== content;
226
+ if (!input.dry_run && changedThisFile) {
225
227
  writeFileSync(safeFile, modified, "utf-8");
226
228
  api.log.info(`auto-doc: updated ${safeFile}`);
227
229
  }
@@ -3,6 +3,11 @@ import { readFileSync } from "node:fs";
3
3
 
4
4
  // src/runtime/index.ts
5
5
  import { basename, extname, isAbsolute, relative, resolve } from "node:path";
6
+
7
+ // src/runtime/local-bin.ts
8
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
9
+
10
+ // src/runtime/index.ts
6
11
  var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
7
12
  function hasLeadingDash(arg) {
8
13
  return arg.length > 0 && arg.startsWith("-");
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/branch-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAqNrD,QAAA,MAAM,MAAM,EAAE,MAuPb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/branch-guard/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA2NrD,QAAA,MAAM,MAAM,EAAE,MAuPb,CAAC;eAEa,MAAM"}
@@ -1,5 +1,118 @@
1
1
  // src/branch-guard/index.ts
2
2
  import { execFile } from "node:child_process";
3
+
4
+ // src/runtime/local-bin.ts
5
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
6
+
7
+ // src/runtime/bounded-map.ts
8
+ var BoundedMap = class {
9
+ map = /* @__PURE__ */ new Map();
10
+ max;
11
+ ttlMs;
12
+ now;
13
+ /** Entries dropped to stay under `max`. Surfaced by plugin health(). */
14
+ evictions = 0;
15
+ constructor(options) {
16
+ const normalizedMax = Math.floor(options.max);
17
+ this.max = Number.isSafeInteger(normalizedMax) ? Math.max(1, normalizedMax) : 1;
18
+ this.ttlMs = options.ttlMs;
19
+ this.now = options.now ?? Date.now;
20
+ }
21
+ expired(entry) {
22
+ return this.ttlMs !== void 0 && this.now() - entry.storedAt > this.ttlMs;
23
+ }
24
+ get(key) {
25
+ const entry = this.map.get(key);
26
+ if (entry === void 0) return void 0;
27
+ if (this.expired(entry)) {
28
+ this.map.delete(key);
29
+ return void 0;
30
+ }
31
+ this.map.delete(key);
32
+ this.map.set(key, entry);
33
+ return entry.value;
34
+ }
35
+ /**
36
+ * Read without promoting the key to most-recently-used. Use for
37
+ * diagnostics that must not perturb the eviction order.
38
+ */
39
+ peek(key) {
40
+ const entry = this.map.get(key);
41
+ if (entry === void 0 || this.expired(entry)) return void 0;
42
+ return entry.value;
43
+ }
44
+ has(key) {
45
+ const entry = this.map.get(key);
46
+ if (entry === void 0) return false;
47
+ if (this.expired(entry)) {
48
+ this.map.delete(key);
49
+ return false;
50
+ }
51
+ return true;
52
+ }
53
+ set(key, value) {
54
+ this.map.delete(key);
55
+ this.map.set(key, { value, storedAt: this.now() });
56
+ while (this.map.size > this.max) {
57
+ const coldest = this.map.keys().next().value;
58
+ if (coldest === void 0) break;
59
+ this.map.delete(coldest);
60
+ this.evictions += 1;
61
+ }
62
+ return this;
63
+ }
64
+ delete(key) {
65
+ return this.map.delete(key);
66
+ }
67
+ clear() {
68
+ this.map.clear();
69
+ this.evictions = 0;
70
+ }
71
+ get size() {
72
+ return this.map.size;
73
+ }
74
+ /** How many entries have been dropped to respect `max`, since the last clear. */
75
+ get evictionCount() {
76
+ return this.evictions;
77
+ }
78
+ /** Drop every expired entry. Cheap enough to call from a status tool. */
79
+ prune() {
80
+ if (this.ttlMs === void 0) return 0;
81
+ let removed = 0;
82
+ for (const [key, entry] of this.map) {
83
+ if (this.expired(entry)) {
84
+ this.map.delete(key);
85
+ removed += 1;
86
+ }
87
+ }
88
+ return removed;
89
+ }
90
+ /** Live (non-expired) entries, coldest first. */
91
+ *entries() {
92
+ for (const [key, entry] of this.map) {
93
+ if (!this.expired(entry)) yield [key, entry.value];
94
+ }
95
+ }
96
+ [Symbol.iterator]() {
97
+ return this.entries();
98
+ }
99
+ };
100
+
101
+ // src/runtime/handles.ts
102
+ function releaseHandle(off) {
103
+ if (off) {
104
+ try {
105
+ off();
106
+ } catch {
107
+ }
108
+ }
109
+ return null;
110
+ }
111
+
112
+ // src/runtime/index.ts
113
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
114
+
115
+ // src/branch-guard/index.ts
3
116
  var API_VERSION = "^0.1.10";
4
117
  var state = {
5
118
  invocationCount: 0,
@@ -51,7 +164,7 @@ function hasDisabledPluginEntry(raw) {
51
164
  return name === "branch-guard" || name === "@wrongstack/plugins/branch-guard";
52
165
  });
53
166
  }
54
- var branchCache = /* @__PURE__ */ new Map();
167
+ var branchCache = new BoundedMap({ max: 64, ttlMs: 3e4 });
55
168
  function runGit(args, cwd, signal) {
56
169
  return new Promise((resolve, reject) => {
57
170
  execFile(
@@ -161,8 +274,8 @@ var plugin = {
161
274
  state.invocationCount = 0;
162
275
  state.blockCount = 0;
163
276
  state.warnCount = 0;
164
- state.hookUnregister = null;
165
- state.configUnregister = null;
277
+ state.hookUnregister = releaseHandle(state.hookUnregister);
278
+ state.configUnregister = releaseHandle(state.configUnregister);
166
279
  state.lastBlock = null;
167
280
  branchCache.clear();
168
281
  let cfg = readHostConfig(api.config);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/changelog-writer/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAMrD,KAAK,OAAO,GACR,OAAO,GACP,SAAS,GACT,OAAO,GACP,SAAS,GACT,UAAU,GACV,eAAe,GACf,aAAa,CAAC;AAElB,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAmGD,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAQjF;AAED,+EAA+E;AAC/E,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOvE;AAeD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,MAAM,CAiBvE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBjF;AAMD,QAAA,MAAM,MAAM,EAAE,MAqRb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/changelog-writer/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAOrD,KAAK,OAAO,GACR,OAAO,GACP,SAAS,GACT,OAAO,GACP,SAAS,GACT,UAAU,GACV,eAAe,GACf,aAAa,CAAC;AAElB,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAwGD,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAQjF;AAED,+EAA+E;AAC/E,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOvE;AAeD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,MAAM,CAiBvE;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBjF;AAMD,QAAA,MAAM,MAAM,EAAE,MAqRb,CAAC;eAEa,MAAM"}
@@ -1,9 +1,143 @@
1
1
  // src/changelog-writer/index.ts
2
2
  import { readFileSync, writeFileSync } from "node:fs";
3
3
  import { isAbsolute, relative, resolve } from "node:path";
4
+
5
+ // src/runtime/local-bin.ts
6
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
7
+
8
+ // src/runtime/bounded-map.ts
9
+ var BoundedMap = class {
10
+ map = /* @__PURE__ */ new Map();
11
+ max;
12
+ ttlMs;
13
+ now;
14
+ /** Entries dropped to stay under `max`. Surfaced by plugin health(). */
15
+ evictions = 0;
16
+ constructor(options) {
17
+ const normalizedMax = Math.floor(options.max);
18
+ this.max = Number.isSafeInteger(normalizedMax) ? Math.max(1, normalizedMax) : 1;
19
+ this.ttlMs = options.ttlMs;
20
+ this.now = options.now ?? Date.now;
21
+ }
22
+ expired(entry) {
23
+ return this.ttlMs !== void 0 && this.now() - entry.storedAt > this.ttlMs;
24
+ }
25
+ get(key) {
26
+ const entry = this.map.get(key);
27
+ if (entry === void 0) return void 0;
28
+ if (this.expired(entry)) {
29
+ this.map.delete(key);
30
+ return void 0;
31
+ }
32
+ this.map.delete(key);
33
+ this.map.set(key, entry);
34
+ return entry.value;
35
+ }
36
+ /**
37
+ * Read without promoting the key to most-recently-used. Use for
38
+ * diagnostics that must not perturb the eviction order.
39
+ */
40
+ peek(key) {
41
+ const entry = this.map.get(key);
42
+ if (entry === void 0 || this.expired(entry)) return void 0;
43
+ return entry.value;
44
+ }
45
+ has(key) {
46
+ const entry = this.map.get(key);
47
+ if (entry === void 0) return false;
48
+ if (this.expired(entry)) {
49
+ this.map.delete(key);
50
+ return false;
51
+ }
52
+ return true;
53
+ }
54
+ set(key, value) {
55
+ this.map.delete(key);
56
+ this.map.set(key, { value, storedAt: this.now() });
57
+ while (this.map.size > this.max) {
58
+ const coldest = this.map.keys().next().value;
59
+ if (coldest === void 0) break;
60
+ this.map.delete(coldest);
61
+ this.evictions += 1;
62
+ }
63
+ return this;
64
+ }
65
+ delete(key) {
66
+ return this.map.delete(key);
67
+ }
68
+ clear() {
69
+ this.map.clear();
70
+ this.evictions = 0;
71
+ }
72
+ get size() {
73
+ return this.map.size;
74
+ }
75
+ /** How many entries have been dropped to respect `max`, since the last clear. */
76
+ get evictionCount() {
77
+ return this.evictions;
78
+ }
79
+ /** Drop every expired entry. Cheap enough to call from a status tool. */
80
+ prune() {
81
+ if (this.ttlMs === void 0) return 0;
82
+ let removed = 0;
83
+ for (const [key, entry] of this.map) {
84
+ if (this.expired(entry)) {
85
+ this.map.delete(key);
86
+ removed += 1;
87
+ }
88
+ }
89
+ return removed;
90
+ }
91
+ /** Live (non-expired) entries, coldest first. */
92
+ *entries() {
93
+ for (const [key, entry] of this.map) {
94
+ if (!this.expired(entry)) yield [key, entry.value];
95
+ }
96
+ }
97
+ [Symbol.iterator]() {
98
+ return this.entries();
99
+ }
100
+ };
101
+ var BoundedSet = class {
102
+ inner;
103
+ constructor(options) {
104
+ this.inner = new BoundedMap(options);
105
+ }
106
+ has(value) {
107
+ return this.inner.has(value);
108
+ }
109
+ add(value) {
110
+ this.inner.set(value, true);
111
+ return this;
112
+ }
113
+ delete(value) {
114
+ return this.inner.delete(value);
115
+ }
116
+ clear() {
117
+ this.inner.clear();
118
+ }
119
+ get size() {
120
+ return this.inner.size;
121
+ }
122
+ /** How many entries have been dropped to respect `max`, since the last clear. */
123
+ get evictionCount() {
124
+ return this.inner.evictionCount;
125
+ }
126
+ *values() {
127
+ for (const [key] of this.inner) yield key;
128
+ }
129
+ [Symbol.iterator]() {
130
+ return this.values();
131
+ }
132
+ };
133
+
134
+ // src/runtime/index.ts
135
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
136
+
137
+ // src/changelog-writer/index.ts
4
138
  var state = {
5
139
  entries: [],
6
- filesTouched: /* @__PURE__ */ new Set(),
140
+ filesTouched: new BoundedSet({ max: 2e3 }),
7
141
  commitsSeen: 0,
8
142
  writes: 0,
9
143
  polishes: 0,
@@ -164,7 +298,7 @@ var plugin = {
164
298
  },
165
299
  setup(api) {
166
300
  state.entries = [];
167
- state.filesTouched = /* @__PURE__ */ new Set();
301
+ state.filesTouched = new BoundedSet({ max: 2e3 });
168
302
  state.commitsSeen = 0;
169
303
  state.writes = 0;
170
304
  for (const off of state.eventUnsubscribers) {
@@ -356,7 +490,7 @@ var plugin = {
356
490
  filesTouched: state.filesTouched.size
357
491
  };
358
492
  state.entries = [];
359
- state.filesTouched = /* @__PURE__ */ new Set();
493
+ state.filesTouched = new BoundedSet({ max: 2e3 });
360
494
  state.commitsSeen = 0;
361
495
  state.writes = 0;
362
496
  api.log.info("changelog-writer: teardown complete", { final });
@@ -48,6 +48,8 @@ export interface Snapshot {
48
48
  bytes: number;
49
49
  }>;
50
50
  }
51
+ /** Retained bytes across all snapshots — surfaced by status/health(). */
52
+ export declare function retainedSnapshotBytes(): number;
51
53
  declare const plugin: Plugin;
52
54
  export default plugin;
53
55
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/checkpoint/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAMrD,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,iDAAiD;QACjD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;CACJ;AAkID,QAAA,MAAM,MAAM,EAAE,MAwUb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/checkpoint/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAMrD,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,iDAAiD;QACjD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;CACJ;AAsID,yEAAyE;AACzE,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAuBD,QAAA,MAAM,MAAM,EAAE,MAmVb,CAAC;eAEa,MAAM"}
@@ -7,13 +7,15 @@ var state = {
7
7
  captures: 0,
8
8
  restores: 0,
9
9
  skippedLarge: 0,
10
+ evictedForBytes: 0,
10
11
  hookUnregister: null
11
12
  };
12
13
  var DEFAULTS = {
13
14
  enabled: true,
14
15
  autoCapture: true,
15
16
  maxSnapshots: 50,
16
- maxFileBytes: 1048576
17
+ maxFileBytes: 1048576,
18
+ maxTotalBytes: 64 * 1048576
17
19
  };
18
20
  function readConfig(raw) {
19
21
  if (!raw || typeof raw !== "object") return { ...DEFAULTS };
@@ -22,6 +24,7 @@ function readConfig(raw) {
22
24
  enabled: r["enabled"] !== false,
23
25
  autoCapture: r["autoCapture"] !== false,
24
26
  maxSnapshots: typeof r["maxSnapshots"] === "number" && r["maxSnapshots"] >= 1 && r["maxSnapshots"] <= 500 ? r["maxSnapshots"] : DEFAULTS.maxSnapshots,
27
+ maxTotalBytes: typeof r["maxTotalBytes"] === "number" && r["maxTotalBytes"] >= 1024 ? r["maxTotalBytes"] : DEFAULTS.maxTotalBytes,
25
28
  maxFileBytes: typeof r["maxFileBytes"] === "number" && r["maxFileBytes"] >= 1024 ? r["maxFileBytes"] : DEFAULTS.maxFileBytes
26
29
  };
27
30
  }
@@ -63,11 +66,22 @@ async function captureFileForHook(path, maxBytes, signal) {
63
66
  return { path, content: null, bytes: 0 };
64
67
  }
65
68
  }
66
- function pushSnapshot(snapshot, maxSnapshots) {
69
+ function retainedSnapshotBytes() {
70
+ let total = 0;
71
+ for (const snap of state.snapshots) {
72
+ for (const f of snap.files) total += f.content === null ? 0 : f.content.length;
73
+ }
74
+ return total;
75
+ }
76
+ function pushSnapshot(snapshot, maxSnapshots, maxTotalBytes) {
67
77
  state.snapshots.push(snapshot);
68
78
  if (state.snapshots.length > maxSnapshots) {
69
79
  state.snapshots.splice(0, state.snapshots.length - maxSnapshots);
70
80
  }
81
+ while (state.snapshots.length > 1 && retainedSnapshotBytes() > maxTotalBytes) {
82
+ state.snapshots.shift();
83
+ state.evictedForBytes += 1;
84
+ }
71
85
  }
72
86
  var plugin = {
73
87
  name: "checkpoint",
@@ -106,6 +120,7 @@ var plugin = {
106
120
  state.captures = 0;
107
121
  state.restores = 0;
108
122
  state.skippedLarge = 0;
123
+ state.evictedForBytes = 0;
109
124
  if (state.hookUnregister) {
110
125
  try {
111
126
  state.hookUnregister();
@@ -133,7 +148,8 @@ var plugin = {
133
148
  origin: `auto:${input.toolName ?? "unknown"}`,
134
149
  files: [captured]
135
150
  },
136
- cfg.maxSnapshots
151
+ cfg.maxSnapshots,
152
+ cfg.maxTotalBytes
137
153
  );
138
154
  state.captures += 1;
139
155
  api.metrics.counter("captures");
@@ -211,7 +227,7 @@ var plugin = {
211
227
  origin: input.label?.trim() ? `manual:${input.label.trim()}` : "manual",
212
228
  files
213
229
  };
214
- pushSnapshot(snapshot, cfg.maxSnapshots);
230
+ pushSnapshot(snapshot, cfg.maxSnapshots, cfg.maxTotalBytes);
215
231
  state.captures += 1;
216
232
  api.metrics.counter("captures");
217
233
  return {
@@ -254,7 +270,9 @@ var plugin = {
254
270
  counters: {
255
271
  captures: state.captures,
256
272
  restores: state.restores,
257
- skippedLarge: state.skippedLarge
273
+ skippedLarge: state.skippedLarge,
274
+ retainedBytes: retainedSnapshotBytes(),
275
+ evictedForBytes: state.evictedForBytes
258
276
  }
259
277
  };
260
278
  }
@@ -336,13 +354,16 @@ var plugin = {
336
354
  captures: state.captures,
337
355
  restores: state.restores,
338
356
  skippedLarge: state.skippedLarge,
339
- snapshotsHeld: state.snapshots.length
357
+ snapshotsHeld: state.snapshots.length,
358
+ retainedBytes: retainedSnapshotBytes(),
359
+ evictedForBytes: state.evictedForBytes
340
360
  };
341
361
  state.snapshots = [];
342
362
  state.nextId = 1;
343
363
  state.captures = 0;
344
364
  state.restores = 0;
345
365
  state.skippedLarge = 0;
366
+ state.evictedForBytes = 0;
346
367
  api.log.info("checkpoint: teardown complete", { final });
347
368
  },
348
369
  async health() {
@@ -353,12 +374,17 @@ var plugin = {
353
374
  snapshotsHeld: state.snapshots.length,
354
375
  captures: state.captures,
355
376
  restores: state.restores,
356
- skippedLarge: state.skippedLarge
377
+ skippedLarge: state.skippedLarge,
378
+ // Retained snapshot bytes: the count-based ring alone does not
379
+ // bound memory, so surface the number the byte budget acts on.
380
+ retainedBytes: retainedSnapshotBytes(),
381
+ evictedForBytes: state.evictedForBytes
357
382
  }
358
383
  };
359
384
  }
360
385
  };
361
386
  var checkpoint_default = plugin;
362
387
  export {
363
- checkpoint_default as default
388
+ checkpoint_default as default,
389
+ retainedSnapshotBytes
364
390
  };
@@ -4,6 +4,11 @@ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 }
4
4
 
5
5
  // src/runtime/index.ts
6
6
  import { basename, extname, isAbsolute, relative, resolve } from "node:path";
7
+
8
+ // src/runtime/local-bin.ts
9
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
10
+
11
+ // src/runtime/index.ts
7
12
  var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
8
13
  function hasLeadingDash(arg) {
9
14
  return arg.length > 0 && arg.startsWith("-");
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commit-validator/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAsPrD,QAAA,MAAM,MAAM,EAAE,MAgSb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commit-validator/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAkRrD,QAAA,MAAM,MAAM,EAAE,MAgSb,CAAC;eAEa,MAAM"}