@wrongstack/plugins 0.295.0 → 0.295.1

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 (90) hide show
  1. package/dist/accessibility-auditor/index.d.ts.map +1 -1
  2. package/dist/accessibility-auditor.js +19 -2
  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 +103 -1
  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 +17 -7
  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.js +5 -0
  25. package/dist/dependency-vulnerability-gate/index.d.ts.map +1 -1
  26. package/dist/dependency-vulnerability-gate.js +43 -8
  27. package/dist/diff-summary/index.d.ts.map +1 -1
  28. package/dist/diff-summary.js +100 -1
  29. package/dist/doc-sync-guard.js +5 -0
  30. package/dist/duplicate-code-detector/index.d.ts.map +1 -1
  31. package/dist/duplicate-code-detector.js +102 -2
  32. package/dist/feature-flag-tracker/index.d.ts.map +1 -1
  33. package/dist/feature-flag-tracker.js +30 -4
  34. package/dist/file-watcher/index.d.ts.map +1 -1
  35. package/dist/file-watcher.js +6 -7
  36. package/dist/format-on-save/index.d.ts +2 -0
  37. package/dist/format-on-save/index.d.ts.map +1 -1
  38. package/dist/format-on-save.js +288 -59
  39. package/dist/import-organizer/index.d.ts.map +1 -1
  40. package/dist/import-organizer.js +140 -20
  41. package/dist/index.js +1265 -544
  42. package/dist/interface-contract-guard/index.d.ts.map +1 -1
  43. package/dist/interface-contract-guard.js +60 -26
  44. package/dist/knowledge-graph/index.d.ts.map +1 -1
  45. package/dist/knowledge-graph.js +7 -6
  46. package/dist/lint-gate/index.d.ts.map +1 -1
  47. package/dist/lint-gate.js +103 -1
  48. package/dist/loop-breaker/index.d.ts.map +1 -1
  49. package/dist/loop-breaker.js +15 -5
  50. package/dist/migration-planner.js +3 -0
  51. package/dist/notify-hub/index.d.ts.map +1 -1
  52. package/dist/notify-hub.js +41 -4
  53. package/dist/prompt-firewall/index.d.ts.map +1 -1
  54. package/dist/prompt-firewall.js +140 -11
  55. package/dist/refactor-suggester/index.d.ts.map +1 -1
  56. package/dist/refactor-suggester.js +119 -4
  57. package/dist/runtime/bounded-map.d.ts +86 -0
  58. package/dist/runtime/bounded-map.d.ts.map +1 -0
  59. package/dist/runtime/credential-patterns.d.ts +42 -0
  60. package/dist/runtime/credential-patterns.d.ts.map +1 -0
  61. package/dist/runtime/index.d.ts +3 -0
  62. package/dist/runtime/index.d.ts.map +1 -1
  63. package/dist/runtime/local-bin.d.ts +120 -0
  64. package/dist/runtime/local-bin.d.ts.map +1 -0
  65. package/dist/runtime/safe-json.d.ts +25 -0
  66. package/dist/runtime/safe-json.d.ts.map +1 -0
  67. package/dist/runtime.js +291 -22
  68. package/dist/schema-evolution-guard.js +5 -0
  69. package/dist/secret-scanner/index.d.ts.map +1 -1
  70. package/dist/secret-scanner.js +175 -48
  71. package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
  72. package/dist/security-hotspot-scanner.js +132 -1
  73. package/dist/semantic-search-indexer/index.d.ts.map +1 -1
  74. package/dist/semantic-search-indexer.js +7 -1
  75. package/dist/semver-bump/index.d.ts.map +1 -1
  76. package/dist/semver-bump.js +21 -2
  77. package/dist/template-engine/index.d.ts.map +1 -1
  78. package/dist/template-engine.js +30 -2
  79. package/dist/test-coverage-gate.js +5 -0
  80. package/dist/test-runner-gate/index.d.ts.map +1 -1
  81. package/dist/test-runner-gate.js +130 -8
  82. package/dist/todo-listener/index.d.ts.map +1 -1
  83. package/dist/todo-listener.js +30 -1
  84. package/dist/todo-tracker/index.d.ts.map +1 -1
  85. package/dist/todo-tracker.js +3 -4
  86. package/dist/token-throttle/index.d.ts.map +1 -1
  87. package/dist/token-throttle.js +17 -2
  88. package/dist/type-gate/index.d.ts.map +1 -1
  89. package/dist/type-gate.js +96 -16
  90. 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,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("-");
@@ -204,15 +209,23 @@ async function auditPath(rawPath, cfg) {
204
209
  const exts = normalizeExtensions(cfg.includeExtensions);
205
210
  const files = await collectSourceFilesAsync(resolved, { extensions: exts });
206
211
  const findings = [];
212
+ let scannedFiles = 0;
213
+ let truncated = false;
207
214
  for (const file of files) {
208
215
  const fileFindings = await auditFile(file, root);
216
+ scannedFiles += 1;
209
217
  findings.push(...fileFindings);
210
- if (findings.length >= cfg.maxFindings) break;
218
+ if (findings.length >= cfg.maxFindings) {
219
+ truncated = scannedFiles < files.length;
220
+ break;
221
+ }
211
222
  }
212
223
  return {
213
224
  path: relative2(root, resolved),
214
225
  findings: findings.slice(0, cfg.maxFindings),
215
- fileCount: files.length
226
+ fileCount: files.length,
227
+ scannedFiles,
228
+ truncated
216
229
  };
217
230
  }
218
231
  function formatSummary(result) {
@@ -341,6 +354,10 @@ var plugin = {
341
354
  ok: true,
342
355
  path: result.path,
343
356
  fileCount: result.fileCount,
357
+ scannedFiles: result.scannedFiles,
358
+ // Say so when the cap stopped the walk early: a partial scan
359
+ // that reports few findings must not read as a clean result.
360
+ truncated: result.truncated,
344
361
  findingCount: result.findings.length,
345
362
  findings: result.findings
346
363
  };
@@ -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,107 @@
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/index.ts
102
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
103
+
104
+ // src/branch-guard/index.ts
3
105
  var API_VERSION = "^0.1.10";
4
106
  var state = {
5
107
  invocationCount: 0,
@@ -51,7 +153,7 @@ function hasDisabledPluginEntry(raw) {
51
153
  return name === "branch-guard" || name === "@wrongstack/plugins/branch-guard";
52
154
  });
53
155
  }
54
- var branchCache = /* @__PURE__ */ new Map();
156
+ var branchCache = new BoundedMap({ max: 64, ttlMs: 3e4 });
55
157
  function runGit(args, cwd, signal) {
56
158
  return new Promise((resolve, reject) => {
57
159
  execFile(
@@ -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;AAiRrD,QAAA,MAAM,MAAM,EAAE,MAgSb,CAAC;eAEa,MAAM"}
@@ -116,14 +116,24 @@ function parseCommitMessage(message, cfg) {
116
116
  errors
117
117
  };
118
118
  }
119
+ var GIT_MESSAGE_FLAG_RE = new RegExp(
120
+ [
121
+ // -m "…" | -m '…' | -m=… ; --message "…" | --message='…' | --message=…
122
+ String.raw`(?:^|\s)(?:-m|--message)(?:\s+|=)"([^"]*)"`,
123
+ String.raw`(?:^|\s)(?:-m|--message)(?:\s+|=)'([^']*)'`,
124
+ // Bare value: stops at whitespace or a shell separator.
125
+ String.raw`(?:^|\s)(?:-m|--message)(?:\s+|=)([^\s;&|"']+)`
126
+ ].join("|"),
127
+ "g"
128
+ );
119
129
  function extractMessageFromBash(command) {
120
- const flags = [];
121
- const doubleQuoted = command.matchAll(/-m\s+"([^"]*)"/g);
122
- const singleQuoted = command.matchAll(/-m\s+'([^']*)'/g);
123
- for (const m of doubleQuoted) flags.push(m[1] ?? "");
124
- for (const m of singleQuoted) flags.push(m[1] ?? "");
125
- if (flags.length === 0) return null;
126
- return flags.join("\n");
130
+ const parts = [];
131
+ for (const m of command.matchAll(GIT_MESSAGE_FLAG_RE)) {
132
+ const value = m[1] ?? m[2] ?? m[3];
133
+ if (value !== void 0) parts.push(value);
134
+ }
135
+ if (parts.length === 0) return null;
136
+ return parts.join("\n");
127
137
  }
128
138
  var plugin = {
129
139
  name: "commit-validator",
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context-pins/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAMrD,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AA8GD,QAAA,MAAM,MAAM,EAAE,MA8Mb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/context-pins/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAOrD,MAAM,WAAW,GAAG;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAoHD,QAAA,MAAM,MAAM,EAAE,MA8Mb,CAAC;eAEa,MAAM"}
@@ -1,6 +1,7 @@
1
1
  // src/context-pins/index.ts
2
2
  import * as fs from "node:fs";
3
3
  import { dirname, isAbsolute, relative, resolve } from "node:path";
4
+ import { atomicWrite, ensureDir } from "@wrongstack/core/utils";
4
5
  var state = {
5
6
  pins: [],
6
7
  nextId: 1,
@@ -47,11 +48,11 @@ function loadPins(filePath) {
47
48
  return { pins: [], nextId: 1 };
48
49
  }
49
50
  }
50
- function persistPins(filePath) {
51
+ async function persistPins(filePath) {
51
52
  if (!filePath) return true;
52
53
  try {
53
- fs.mkdirSync(dirname(filePath), { recursive: true });
54
- fs.writeFileSync(filePath, JSON.stringify({ pins: state.pins, nextId: state.nextId }, null, 2));
54
+ await ensureDir(dirname(filePath));
55
+ await atomicWrite(filePath, JSON.stringify({ pins: state.pins, nextId: state.nextId }, null, 2));
55
56
  return true;
56
57
  } catch {
57
58
  state.persistErrors += 1;
@@ -149,7 +150,7 @@ var plugin = {
149
150
  state.pins.push(pin);
150
151
  state.adds += 1;
151
152
  api.metrics.counter("adds");
152
- const persisted = persistPins(cfg.filePath);
153
+ const persisted = await persistPins(cfg.filePath);
153
154
  return { ok: true, pin, persisted, totalPins: state.pins.length };
154
155
  }
155
156
  });
@@ -175,7 +176,7 @@ var plugin = {
175
176
  if (removed === 0) return { ok: false, error: `no pin matches "${key}"` };
176
177
  state.removals += removed;
177
178
  api.metrics.counter("removals", removed);
178
- const persisted = persistPins(cfg.filePath);
179
+ const persisted = await persistPins(cfg.filePath);
179
180
  return { ok: true, removed, persisted, totalPins: state.pins.length };
180
181
  }
181
182
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cost-tracker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAKrD;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB;AAoLD,QAAA,MAAM,MAAM,EAAE,MAoab,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cost-tracker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAMrD;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;CAChB;AAuLD,QAAA,MAAM,MAAM,EAAE,MAoab,CAAC;eAEa,MAAM"}