@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,3 +1,103 @@
1
+ // src/runtime/local-bin.ts
2
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
3
+
4
+ // src/runtime/bounded-map.ts
5
+ var BoundedMap = class {
6
+ map = /* @__PURE__ */ new Map();
7
+ max;
8
+ ttlMs;
9
+ now;
10
+ /** Entries dropped to stay under `max`. Surfaced by plugin health(). */
11
+ evictions = 0;
12
+ constructor(options) {
13
+ const normalizedMax = Math.floor(options.max);
14
+ this.max = Number.isSafeInteger(normalizedMax) ? Math.max(1, normalizedMax) : 1;
15
+ this.ttlMs = options.ttlMs;
16
+ this.now = options.now ?? Date.now;
17
+ }
18
+ expired(entry) {
19
+ return this.ttlMs !== void 0 && this.now() - entry.storedAt > this.ttlMs;
20
+ }
21
+ get(key) {
22
+ const entry = this.map.get(key);
23
+ if (entry === void 0) return void 0;
24
+ if (this.expired(entry)) {
25
+ this.map.delete(key);
26
+ return void 0;
27
+ }
28
+ this.map.delete(key);
29
+ this.map.set(key, entry);
30
+ return entry.value;
31
+ }
32
+ /**
33
+ * Read without promoting the key to most-recently-used. Use for
34
+ * diagnostics that must not perturb the eviction order.
35
+ */
36
+ peek(key) {
37
+ const entry = this.map.get(key);
38
+ if (entry === void 0 || this.expired(entry)) return void 0;
39
+ return entry.value;
40
+ }
41
+ has(key) {
42
+ const entry = this.map.get(key);
43
+ if (entry === void 0) return false;
44
+ if (this.expired(entry)) {
45
+ this.map.delete(key);
46
+ return false;
47
+ }
48
+ return true;
49
+ }
50
+ set(key, value) {
51
+ this.map.delete(key);
52
+ this.map.set(key, { value, storedAt: this.now() });
53
+ while (this.map.size > this.max) {
54
+ const coldest = this.map.keys().next().value;
55
+ if (coldest === void 0) break;
56
+ this.map.delete(coldest);
57
+ this.evictions += 1;
58
+ }
59
+ return this;
60
+ }
61
+ delete(key) {
62
+ return this.map.delete(key);
63
+ }
64
+ clear() {
65
+ this.map.clear();
66
+ this.evictions = 0;
67
+ }
68
+ get size() {
69
+ return this.map.size;
70
+ }
71
+ /** How many entries have been dropped to respect `max`, since the last clear. */
72
+ get evictionCount() {
73
+ return this.evictions;
74
+ }
75
+ /** Drop every expired entry. Cheap enough to call from a status tool. */
76
+ prune() {
77
+ if (this.ttlMs === void 0) return 0;
78
+ let removed = 0;
79
+ for (const [key, entry] of this.map) {
80
+ if (this.expired(entry)) {
81
+ this.map.delete(key);
82
+ removed += 1;
83
+ }
84
+ }
85
+ return removed;
86
+ }
87
+ /** Live (non-expired) entries, coldest first. */
88
+ *entries() {
89
+ for (const [key, entry] of this.map) {
90
+ if (!this.expired(entry)) yield [key, entry.value];
91
+ }
92
+ }
93
+ [Symbol.iterator]() {
94
+ return this.entries();
95
+ }
96
+ };
97
+
98
+ // src/runtime/index.ts
99
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
100
+
1
101
  // src/cost-tracker/index.ts
2
102
  import { expectDefined } from "@wrongstack/core/utils";
3
103
  var API_VERSION = "^0.1.10";
@@ -28,7 +128,7 @@ function readCostTrackerConfig(raw) {
28
128
  mailboxDigestTo: typeof digestTo === "string" && digestTo.length > 0 ? digestTo : "cost-tracker"
29
129
  };
30
130
  }
31
- var modelKeyCache = /* @__PURE__ */ new Map();
131
+ var modelKeyCache = new BoundedMap({ max: 256 });
32
132
  function estimateCost(model, promptTokens, completionTokens) {
33
133
  let key = modelKeyCache.get(model);
34
134
  if (!key) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cron/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA+FrD,QAAA,MAAM,MAAM,EAAE,MAqRb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cron/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA+FrD,QAAA,MAAM,MAAM,EAAE,MA2Rb,CAAC;eAEa,MAAM"}
package/dist/cron.js CHANGED
@@ -82,6 +82,7 @@ var plugin = {
82
82
  api.metrics.histogram("cron_job_interval_ms", job.intervalMs, { job: name });
83
83
  scheduleNextRun(name);
84
84
  }, delay);
85
+ timer.unref?.();
85
86
  state.timers.set(name, timer);
86
87
  }
87
88
  function cancelJob(name) {
@@ -4,6 +4,11 @@ import { extname as extname2, join, 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/dependency-vulnerability-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAqPrD,QAAA,MAAM,MAAM,EAAE,MAwMb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dependency-vulnerability-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA2RrD,QAAA,MAAM,MAAM,EAAE,MAwMb,CAAC;eAEa,MAAM"}
@@ -1,6 +1,24 @@
1
1
  // src/dependency-vulnerability-gate/index.ts
2
2
  import { execFile } from "node:child_process";
3
3
  import { access } from "node:fs/promises";
4
+
5
+ // src/runtime/local-bin.ts
6
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
7
+ function resolveExecInvocation(command, args = []) {
8
+ const resolved = resolveWin32Command(command);
9
+ const normalizedResolved = resolved.toLowerCase();
10
+ const needsShell = process.platform === "win32" && (normalizedResolved.endsWith(".cmd") || normalizedResolved.endsWith(".bat"));
11
+ if (needsShell) {
12
+ const shim = buildWin32CmdShimInvocation(resolved, args);
13
+ return { cmd: shim.command, args: shim.args, windowsVerbatimArguments: true };
14
+ }
15
+ return { cmd: resolved, args: [...args], windowsVerbatimArguments: false };
16
+ }
17
+
18
+ // src/runtime/index.ts
19
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
20
+
21
+ // src/dependency-vulnerability-gate/index.ts
4
22
  var API_VERSION = "^0.1.10";
5
23
  var state = {
6
24
  invocations: 0,
@@ -112,27 +130,44 @@ function isInstallCommand(input) {
112
130
  const command = typeof ti["command"] === "string" ? ti["command"] : "";
113
131
  return INSTALL_RE.test(command);
114
132
  }
133
+ var LOCKFILE_MANAGERS = [
134
+ { file: "pnpm-lock.yaml", manager: "pnpm" },
135
+ { file: "bun.lockb", manager: "bun" },
136
+ { file: "bun.lock", manager: "bun" },
137
+ { file: "yarn.lock", manager: "yarn" },
138
+ { file: "package-lock.json", manager: "npm" }
139
+ ];
115
140
  async function detectManager() {
116
- try {
117
- await access("pnpm-lock.yaml");
118
- return "pnpm";
119
- } catch {
120
- return "npm";
141
+ for (const { file, manager } of LOCKFILE_MANAGERS) {
142
+ try {
143
+ await access(file);
144
+ return manager;
145
+ } catch {
146
+ }
121
147
  }
148
+ return "npm";
122
149
  }
123
150
  async function runAudit(cfg) {
124
151
  const manager = await detectManager();
125
152
  const start = Date.now();
153
+ let invocation;
154
+ try {
155
+ invocation = resolveExecInvocation(manager, ["audit", "--json"]);
156
+ } catch {
157
+ return null;
158
+ }
126
159
  const stdout = await new Promise((resolve) => {
127
160
  execFile(
128
- manager,
129
- ["audit", "--json"],
161
+ invocation.cmd,
162
+ invocation.args,
130
163
  {
131
164
  encoding: "utf8",
132
165
  timeout: cfg.timeoutMs,
133
166
  cwd: process.cwd(),
134
167
  windowsHide: true,
135
- maxBuffer: 10 * 1024 * 1024
168
+ maxBuffer: 10 * 1024 * 1024,
169
+ shell: false,
170
+ ...invocation.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
136
171
  },
137
172
  (error, output) => {
138
173
  if (error && error.killed) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/diff-summary/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA2PrD,QAAA,MAAM,MAAM,EAAE,MA4Pb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/diff-summary/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA+PrD,QAAA,MAAM,MAAM,EAAE,MA4Pb,CAAC;eAEa,MAAM"}
@@ -3,6 +3,105 @@ import { execFile } from "node:child_process";
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/bounded-map.ts
11
+ var BoundedMap = class {
12
+ map = /* @__PURE__ */ new Map();
13
+ max;
14
+ ttlMs;
15
+ now;
16
+ /** Entries dropped to stay under `max`. Surfaced by plugin health(). */
17
+ evictions = 0;
18
+ constructor(options) {
19
+ const normalizedMax = Math.floor(options.max);
20
+ this.max = Number.isSafeInteger(normalizedMax) ? Math.max(1, normalizedMax) : 1;
21
+ this.ttlMs = options.ttlMs;
22
+ this.now = options.now ?? Date.now;
23
+ }
24
+ expired(entry) {
25
+ return this.ttlMs !== void 0 && this.now() - entry.storedAt > this.ttlMs;
26
+ }
27
+ get(key) {
28
+ const entry = this.map.get(key);
29
+ if (entry === void 0) return void 0;
30
+ if (this.expired(entry)) {
31
+ this.map.delete(key);
32
+ return void 0;
33
+ }
34
+ this.map.delete(key);
35
+ this.map.set(key, entry);
36
+ return entry.value;
37
+ }
38
+ /**
39
+ * Read without promoting the key to most-recently-used. Use for
40
+ * diagnostics that must not perturb the eviction order.
41
+ */
42
+ peek(key) {
43
+ const entry = this.map.get(key);
44
+ if (entry === void 0 || this.expired(entry)) return void 0;
45
+ return entry.value;
46
+ }
47
+ has(key) {
48
+ const entry = this.map.get(key);
49
+ if (entry === void 0) return false;
50
+ if (this.expired(entry)) {
51
+ this.map.delete(key);
52
+ return false;
53
+ }
54
+ return true;
55
+ }
56
+ set(key, value) {
57
+ this.map.delete(key);
58
+ this.map.set(key, { value, storedAt: this.now() });
59
+ while (this.map.size > this.max) {
60
+ const coldest = this.map.keys().next().value;
61
+ if (coldest === void 0) break;
62
+ this.map.delete(coldest);
63
+ this.evictions += 1;
64
+ }
65
+ return this;
66
+ }
67
+ delete(key) {
68
+ return this.map.delete(key);
69
+ }
70
+ clear() {
71
+ this.map.clear();
72
+ this.evictions = 0;
73
+ }
74
+ get size() {
75
+ return this.map.size;
76
+ }
77
+ /** How many entries have been dropped to respect `max`, since the last clear. */
78
+ get evictionCount() {
79
+ return this.evictions;
80
+ }
81
+ /** Drop every expired entry. Cheap enough to call from a status tool. */
82
+ prune() {
83
+ if (this.ttlMs === void 0) return 0;
84
+ let removed = 0;
85
+ for (const [key, entry] of this.map) {
86
+ if (this.expired(entry)) {
87
+ this.map.delete(key);
88
+ removed += 1;
89
+ }
90
+ }
91
+ return removed;
92
+ }
93
+ /** Live (non-expired) entries, coldest first. */
94
+ *entries() {
95
+ for (const [key, entry] of this.map) {
96
+ if (!this.expired(entry)) yield [key, entry.value];
97
+ }
98
+ }
99
+ [Symbol.iterator]() {
100
+ return this.entries();
101
+ }
102
+ };
103
+
104
+ // src/runtime/index.ts
6
105
  var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
7
106
  function hasLeadingDash(arg) {
8
107
  return arg.length > 0 && arg.startsWith("-");
@@ -64,7 +163,7 @@ function contentHash(s) {
64
163
  }
65
164
  return h >>> 0;
66
165
  }
67
- var pathMemo = /* @__PURE__ */ new Map();
166
+ var pathMemo = new BoundedMap({ max: 512 });
68
167
  function runGit(args, cwd) {
69
168
  return new Promise((resolveCommand) => {
70
169
  execFile(
@@ -3,6 +3,11 @@ import { basename as basename2, extname as extname2 } from "node:path";
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/duplicate-code-detector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA8ErD;;;GAGG;AACH,eAAO,MAAM,gBAAgB;IAC3B,gFAAgF;IAChF,QAAQ;IACR,yFAAyF;IACzF,eAAe;IACf;;;OAGG;IACH,sBAAsB;IACtB,6FAA6F;IAC7F,YAAY;CACb,CAAC;AAyGF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAelD;AA4HD,QAAA,MAAM,MAAM,EAAE,MA4Wb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/duplicate-code-detector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA8FrD;;;GAGG;AACH,eAAO,MAAM,gBAAgB;IAC3B,gFAAgF;IAChF,QAAQ;IACR,yFAAyF;IACzF,eAAe;IACf;;;OAGG;IACH,sBAAsB;IACtB,6FAA6F;IAC7F,YAAY;CACb,CAAC;AAyGF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAelD;AA4HD,QAAA,MAAM,MAAM,EAAE,MA4Wb,CAAC;eAEa,MAAM"}
@@ -4,6 +4,105 @@ 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/bounded-map.ts
12
+ var BoundedMap = class {
13
+ map = /* @__PURE__ */ new Map();
14
+ max;
15
+ ttlMs;
16
+ now;
17
+ /** Entries dropped to stay under `max`. Surfaced by plugin health(). */
18
+ evictions = 0;
19
+ constructor(options) {
20
+ const normalizedMax = Math.floor(options.max);
21
+ this.max = Number.isSafeInteger(normalizedMax) ? Math.max(1, normalizedMax) : 1;
22
+ this.ttlMs = options.ttlMs;
23
+ this.now = options.now ?? Date.now;
24
+ }
25
+ expired(entry) {
26
+ return this.ttlMs !== void 0 && this.now() - entry.storedAt > this.ttlMs;
27
+ }
28
+ get(key) {
29
+ const entry = this.map.get(key);
30
+ if (entry === void 0) return void 0;
31
+ if (this.expired(entry)) {
32
+ this.map.delete(key);
33
+ return void 0;
34
+ }
35
+ this.map.delete(key);
36
+ this.map.set(key, entry);
37
+ return entry.value;
38
+ }
39
+ /**
40
+ * Read without promoting the key to most-recently-used. Use for
41
+ * diagnostics that must not perturb the eviction order.
42
+ */
43
+ peek(key) {
44
+ const entry = this.map.get(key);
45
+ if (entry === void 0 || this.expired(entry)) return void 0;
46
+ return entry.value;
47
+ }
48
+ has(key) {
49
+ const entry = this.map.get(key);
50
+ if (entry === void 0) return false;
51
+ if (this.expired(entry)) {
52
+ this.map.delete(key);
53
+ return false;
54
+ }
55
+ return true;
56
+ }
57
+ set(key, value) {
58
+ this.map.delete(key);
59
+ this.map.set(key, { value, storedAt: this.now() });
60
+ while (this.map.size > this.max) {
61
+ const coldest = this.map.keys().next().value;
62
+ if (coldest === void 0) break;
63
+ this.map.delete(coldest);
64
+ this.evictions += 1;
65
+ }
66
+ return this;
67
+ }
68
+ delete(key) {
69
+ return this.map.delete(key);
70
+ }
71
+ clear() {
72
+ this.map.clear();
73
+ this.evictions = 0;
74
+ }
75
+ get size() {
76
+ return this.map.size;
77
+ }
78
+ /** How many entries have been dropped to respect `max`, since the last clear. */
79
+ get evictionCount() {
80
+ return this.evictions;
81
+ }
82
+ /** Drop every expired entry. Cheap enough to call from a status tool. */
83
+ prune() {
84
+ if (this.ttlMs === void 0) return 0;
85
+ let removed = 0;
86
+ for (const [key, entry] of this.map) {
87
+ if (this.expired(entry)) {
88
+ this.map.delete(key);
89
+ removed += 1;
90
+ }
91
+ }
92
+ return removed;
93
+ }
94
+ /** Live (non-expired) entries, coldest first. */
95
+ *entries() {
96
+ for (const [key, entry] of this.map) {
97
+ if (!this.expired(entry)) yield [key, entry.value];
98
+ }
99
+ }
100
+ [Symbol.iterator]() {
101
+ return this.entries();
102
+ }
103
+ };
104
+
105
+ // src/runtime/index.ts
7
106
  var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
8
107
  function hasLeadingDash(arg) {
9
108
  return arg.length > 0 && arg.startsWith("-");
@@ -63,6 +162,7 @@ function matchesExtension(p, exts) {
63
162
 
64
163
  // src/duplicate-code-detector/index.ts
65
164
  var API_VERSION = "^0.1.10";
165
+ var HOOK_WARNING_COOLDOWN_MS = 6e4;
66
166
  var state = {
67
167
  scanCount: 0,
68
168
  findingCount: 0,
@@ -70,7 +170,7 @@ var state = {
70
170
  warningCount: 0,
71
171
  errorCount: 0,
72
172
  hookUnregister: null,
73
- lastHookWarning: /* @__PURE__ */ new Map(),
173
+ lastHookWarning: new BoundedMap({ max: 512, ttlMs: HOOK_WARNING_COOLDOWN_MS }),
74
174
  fileIndex: /* @__PURE__ */ new Map(),
75
175
  inFlightFingerprintReads: /* @__PURE__ */ new Map(),
76
176
  indexFingerprintCount: 0,
@@ -360,7 +460,7 @@ var plugin = {
360
460
  state.hookInvocationCount += 1;
361
461
  const now = Date.now();
362
462
  const lastWarning = state.lastHookWarning.get(changedFile);
363
- if (lastWarning !== void 0 && now - lastWarning < 6e4) return;
463
+ if (lastWarning !== void 0 && now - lastWarning < HOOK_WARNING_COOLDOWN_MS) return;
364
464
  const changedFps = await readCachedFingerprints(changedFile, cfg.minLines);
365
465
  if (changedFps === null) {
366
466
  state.errorCount += 1;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/feature-flag-tracker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AASrD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AA6ID,QAAA,MAAM,MAAM,EAAE,MA+Mb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/feature-flag-tracker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AASrD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAmLD,QAAA,MAAM,MAAM,EAAE,MAmNb,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("-");
@@ -103,8 +108,14 @@ function relativePath(p) {
103
108
  return toPosix(relative2(process.cwd(), p));
104
109
  }
105
110
  function compilePatterns(patterns) {
106
- const sources = [...DEFAULT_PATTERNS, ...patterns];
107
- return sources.map((src) => new RegExp(src, "g"));
111
+ const out = [];
112
+ for (const src of [...DEFAULT_PATTERNS, ...patterns]) {
113
+ try {
114
+ out.push(new RegExp(src, "g"));
115
+ } catch {
116
+ }
117
+ }
118
+ return out;
108
119
  }
109
120
  function scanFile(filePath, content, patterns, maxFindings) {
110
121
  const usages = [];
@@ -135,15 +146,26 @@ async function scanPath(rawPath, cfg) {
135
146
  const files = await collectSourceFilesAsync(resolved, { extensions: exts });
136
147
  const patterns = compilePatterns(cfg.patterns);
137
148
  const usages = [];
149
+ let scannedFiles = 0;
150
+ let truncated = false;
138
151
  for (const p of files) {
139
152
  try {
140
153
  const content = await readFile(p, "utf-8");
154
+ scannedFiles += 1;
141
155
  usages.push(...scanFile(p, content, patterns, cfg.maxFindings));
142
- if (usages.length >= cfg.maxFindings) break;
156
+ if (usages.length >= cfg.maxFindings) {
157
+ truncated = scannedFiles < files.length;
158
+ break;
159
+ }
143
160
  } catch {
144
161
  }
145
162
  }
146
- return { usages: usages.slice(0, cfg.maxFindings), scannedFiles: files.length };
163
+ return {
164
+ usages: usages.slice(0, cfg.maxFindings),
165
+ scannedFiles,
166
+ discoveredFiles: files.length,
167
+ truncated
168
+ };
147
169
  }
148
170
  var plugin = {
149
171
  name: "feature-flag-tracker",
@@ -252,6 +274,10 @@ Make sure flag behavior is intentional and consider updating flag inventory/docs
252
274
  ok: true,
253
275
  path: relativePath(resolve2(process.cwd(), rawPath)),
254
276
  scannedFiles: result.scannedFiles,
277
+ discoveredFiles: result.discoveredFiles,
278
+ // Say so when the cap stopped the walk early: a partial scan
279
+ // that reports few findings must not read as a clean result.
280
+ truncated: result.truncated,
255
281
  usages: result.usages
256
282
  };
257
283
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/file-watcher/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAqErD,QAAA,MAAM,MAAM,EAAE,MA8Yb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/file-watcher/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAqErD,QAAA,MAAM,MAAM,EAAE,MA+Yb,CAAC;eAEa,MAAM"}
@@ -80,13 +80,12 @@ var plugin = {
80
80
  function debounceEvent(key, fn, ms) {
81
81
  const existing = debounceTimers.get(key);
82
82
  if (existing) clearTimeout(existing);
83
- debounceTimers.set(
84
- key,
85
- setTimeout(() => {
86
- debounceTimers.delete(key);
87
- fn();
88
- }, ms)
89
- );
83
+ const timer = setTimeout(() => {
84
+ debounceTimers.delete(key);
85
+ fn();
86
+ }, ms);
87
+ timer.unref?.();
88
+ debounceTimers.set(key, timer);
90
89
  }
91
90
  const autoIndex = api.config.extensions?.["file-watcher"]?.["autoIndex"] ?? false;
92
91
  const indexProjectRoot = api.config.extensions?.["file-watcher"]?.["indexProjectRoot"] ?? "";
@@ -28,6 +28,8 @@
28
28
  * @public
29
29
  */
30
30
  import type { Plugin } from '@wrongstack/core/types';
31
+ /** Which formatter is currently in use — surfaced by health()/status. */
32
+ export declare function activeFormatterLabel(): string | null;
31
33
  declare const plugin: Plugin;
32
34
  export default plugin;
33
35
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/format-on-save/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA0PrD,QAAA,MAAM,MAAM,EAAE,MAuPb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/format-on-save/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAmPrD,yEAAyE;AACzE,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAEpD;AAsGD,QAAA,MAAM,MAAM,EAAE,MAsPb,CAAC;eAEa,MAAM"}