@wrongstack/plugins 0.293.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 (211) hide show
  1. package/README.md +7 -71
  2. package/dist/accessibility-auditor/index.d.ts +1 -1
  3. package/dist/accessibility-auditor/index.d.ts.map +1 -1
  4. package/dist/accessibility-auditor.js +50 -33
  5. package/dist/agent-handoff/index.d.ts +1 -1
  6. package/dist/agent-handoff/index.d.ts.map +1 -1
  7. package/dist/agent-handoff.js +30 -1
  8. package/dist/api-compatibility-gate/index.d.ts +1 -1
  9. package/dist/api-compatibility-gate/index.d.ts.map +1 -1
  10. package/dist/api-compatibility-gate.js +21 -16
  11. package/dist/audit/index.d.ts +386 -0
  12. package/dist/audit/index.d.ts.map +1 -0
  13. package/dist/audit.d.ts +1 -0
  14. package/dist/audit.js +454 -0
  15. package/dist/auto-doc/index.d.ts +1 -1
  16. package/dist/auto-doc/index.d.ts.map +1 -1
  17. package/dist/auto-doc.js +3 -1
  18. package/dist/auto-escalate/index.d.ts +1 -1
  19. package/dist/auto-escalate/index.d.ts.map +1 -1
  20. package/dist/auto-i18n-extractor/index.d.ts +1 -1
  21. package/dist/auto-i18n-extractor/index.d.ts.map +1 -1
  22. package/dist/auto-i18n-extractor.js +7 -1
  23. package/dist/branch-guard/index.d.ts +1 -1
  24. package/dist/branch-guard/index.d.ts.map +1 -1
  25. package/dist/branch-guard.js +111 -1
  26. package/dist/catalog.d.ts +5 -30
  27. package/dist/catalog.d.ts.map +1 -1
  28. package/dist/changelog-writer/index.d.ts +1 -1
  29. package/dist/changelog-writer/index.d.ts.map +1 -1
  30. package/dist/changelog-writer.js +137 -3
  31. package/dist/checkpoint/index.d.ts +3 -1
  32. package/dist/checkpoint/index.d.ts.map +1 -1
  33. package/dist/checkpoint.js +41 -16
  34. package/dist/code-metrics/index.d.ts +1 -1
  35. package/dist/code-metrics/index.d.ts.map +1 -1
  36. package/dist/code-metrics.js +34 -29
  37. package/dist/commit-validator/index.d.ts +1 -1
  38. package/dist/commit-validator/index.d.ts.map +1 -1
  39. package/dist/commit-validator.js +17 -7
  40. package/dist/config-validator/index.d.ts +1 -1
  41. package/dist/config-validator/index.d.ts.map +1 -1
  42. package/dist/context-pins/index.d.ts +1 -1
  43. package/dist/context-pins/index.d.ts.map +1 -1
  44. package/dist/context-pins.js +6 -5
  45. package/dist/cost-tracker/index.d.ts +1 -1
  46. package/dist/cost-tracker/index.d.ts.map +1 -1
  47. package/dist/cost-tracker.js +109 -2
  48. package/dist/cron/index.d.ts +1 -1
  49. package/dist/cron/index.d.ts.map +1 -1
  50. package/dist/cron.js +1 -0
  51. package/dist/dead-code-detector/index.d.ts +1 -1
  52. package/dist/dead-code-detector/index.d.ts.map +1 -1
  53. package/dist/dead-code-detector.js +31 -18
  54. package/dist/dep-guard/index.d.ts +1 -1
  55. package/dist/dep-guard/index.d.ts.map +1 -1
  56. package/dist/dependency-vulnerability-gate/index.d.ts +1 -1
  57. package/dist/dependency-vulnerability-gate/index.d.ts.map +1 -1
  58. package/dist/dependency-vulnerability-gate.js +67 -20
  59. package/dist/diff-summary/index.d.ts +1 -1
  60. package/dist/diff-summary/index.d.ts.map +1 -1
  61. package/dist/diff-summary.js +128 -30
  62. package/dist/doc-sync-guard/index.d.ts +1 -1
  63. package/dist/doc-sync-guard/index.d.ts.map +1 -1
  64. package/dist/doc-sync-guard.js +7 -1
  65. package/dist/duplicate-code-detector/index.d.ts +26 -1
  66. package/dist/duplicate-code-detector/index.d.ts.map +1 -1
  67. package/dist/duplicate-code-detector.js +280 -69
  68. package/dist/error-lens/index.d.ts +1 -1
  69. package/dist/error-lens/index.d.ts.map +1 -1
  70. package/dist/factories/index.d.ts +4 -0
  71. package/dist/factories/index.d.ts.map +1 -0
  72. package/dist/factories.d.ts +1 -0
  73. package/dist/factories.js +74 -0
  74. package/dist/feature-flag-tracker/index.d.ts +1 -1
  75. package/dist/feature-flag-tracker/index.d.ts.map +1 -1
  76. package/dist/feature-flag-tracker.js +59 -33
  77. package/dist/file-watcher/index.d.ts +1 -1
  78. package/dist/file-watcher/index.d.ts.map +1 -1
  79. package/dist/file-watcher.js +6 -7
  80. package/dist/format-on-save/index.d.ts +3 -1
  81. package/dist/format-on-save/index.d.ts.map +1 -1
  82. package/dist/format-on-save.js +289 -59
  83. package/dist/generated-plugin-exports.d.ts +65 -0
  84. package/dist/generated-plugin-exports.d.ts.map +1 -0
  85. package/dist/git-autocommit/index.d.ts +1 -1
  86. package/dist/git-autocommit/index.d.ts.map +1 -1
  87. package/dist/import-organizer/index.d.ts +1 -1
  88. package/dist/import-organizer/index.d.ts.map +1 -1
  89. package/dist/import-organizer.js +141 -20
  90. package/dist/index.d.ts +4 -183
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +18298 -17279
  93. package/dist/injection-shield/index.d.ts +1 -1
  94. package/dist/injection-shield/index.d.ts.map +1 -1
  95. package/dist/interface-contract-guard/index.d.ts +1 -1
  96. package/dist/interface-contract-guard/index.d.ts.map +1 -1
  97. package/dist/interface-contract-guard.js +87 -53
  98. package/dist/knowledge-graph/index.d.ts +1 -1
  99. package/dist/knowledge-graph/index.d.ts.map +1 -1
  100. package/dist/knowledge-graph.js +7 -6
  101. package/dist/license-audit-gate/index.d.ts +1 -1
  102. package/dist/license-audit-gate/index.d.ts.map +1 -1
  103. package/dist/lint-gate/index.d.ts +1 -1
  104. package/dist/lint-gate/index.d.ts.map +1 -1
  105. package/dist/lint-gate.js +112 -1
  106. package/dist/llm-cache/index.d.ts +5 -1
  107. package/dist/llm-cache/index.d.ts.map +1 -1
  108. package/dist/llm-cache.js +16 -8
  109. package/dist/loop-breaker/index.d.ts +1 -1
  110. package/dist/loop-breaker/index.d.ts.map +1 -1
  111. package/dist/loop-breaker.js +25 -9
  112. package/dist/manifest/index.d.ts +16 -0
  113. package/dist/manifest/index.d.ts.map +1 -0
  114. package/dist/manifest.d.ts +1 -0
  115. package/dist/manifest.js +152 -0
  116. package/dist/migration-planner/index.d.ts +1 -1
  117. package/dist/migration-planner/index.d.ts.map +1 -1
  118. package/dist/migration-planner.js +5 -1
  119. package/dist/model-router/index.d.ts +1 -1
  120. package/dist/model-router/index.d.ts.map +1 -1
  121. package/dist/notify-hub/index.d.ts +1 -1
  122. package/dist/notify-hub/index.d.ts.map +1 -1
  123. package/dist/notify-hub/webhook-channel.d.ts +1 -1
  124. package/dist/notify-hub/webhook-channel.d.ts.map +1 -1
  125. package/dist/notify-hub.js +41 -4
  126. package/dist/path-guard/index.d.ts +1 -1
  127. package/dist/path-guard/index.d.ts.map +1 -1
  128. package/dist/performance-regression-gate/index.d.ts +1 -1
  129. package/dist/performance-regression-gate/index.d.ts.map +1 -1
  130. package/dist/plugin-stack-observer/index.d.ts +1 -1
  131. package/dist/plugin-stack-observer/index.d.ts.map +1 -1
  132. package/dist/pr-drafter/index.d.ts +1 -1
  133. package/dist/pr-drafter/index.d.ts.map +1 -1
  134. package/dist/pr-drafter.js +28 -29
  135. package/dist/process-guard/index.d.ts +1 -1
  136. package/dist/process-guard/index.d.ts.map +1 -1
  137. package/dist/prompt-firewall/index.d.ts +1 -1
  138. package/dist/prompt-firewall/index.d.ts.map +1 -1
  139. package/dist/prompt-firewall.js +140 -11
  140. package/dist/refactor-suggester/index.d.ts +1 -1
  141. package/dist/refactor-suggester/index.d.ts.map +1 -1
  142. package/dist/refactor-suggester.js +148 -33
  143. package/dist/release-notes-generator/index.d.ts +1 -1
  144. package/dist/release-notes-generator/index.d.ts.map +1 -1
  145. package/dist/release-notes-generator.js +29 -22
  146. package/dist/runtime/bounded-map.d.ts +86 -0
  147. package/dist/runtime/bounded-map.d.ts.map +1 -0
  148. package/dist/runtime/credential-patterns.d.ts +42 -0
  149. package/dist/runtime/credential-patterns.d.ts.map +1 -0
  150. package/dist/runtime/index.d.ts +18 -0
  151. package/dist/runtime/index.d.ts.map +1 -1
  152. package/dist/runtime/llm.d.ts +1 -1
  153. package/dist/runtime/llm.d.ts.map +1 -1
  154. package/dist/runtime/local-bin.d.ts +120 -0
  155. package/dist/runtime/local-bin.d.ts.map +1 -0
  156. package/dist/runtime/safe-json.d.ts +25 -0
  157. package/dist/runtime/safe-json.d.ts.map +1 -0
  158. package/dist/runtime.js +332 -22
  159. package/dist/schema-evolution-guard/index.d.ts +1 -1
  160. package/dist/schema-evolution-guard/index.d.ts.map +1 -1
  161. package/dist/schema-evolution-guard.js +7 -1
  162. package/dist/secret-scanner/index.d.ts +1 -1
  163. package/dist/secret-scanner/index.d.ts.map +1 -1
  164. package/dist/secret-scanner.js +181 -48
  165. package/dist/security-hotspot-scanner/index.d.ts +1 -1
  166. package/dist/security-hotspot-scanner/index.d.ts.map +1 -1
  167. package/dist/security-hotspot-scanner.js +158 -20
  168. package/dist/semantic-search-indexer/index.d.ts +1 -1
  169. package/dist/semantic-search-indexer/index.d.ts.map +1 -1
  170. package/dist/semantic-search-indexer.js +7 -1
  171. package/dist/semver-bump/index.d.ts +1 -1
  172. package/dist/semver-bump/index.d.ts.map +1 -1
  173. package/dist/semver-bump.js +97 -43
  174. package/dist/session-recap/index.d.ts +1 -1
  175. package/dist/session-recap/index.d.ts.map +1 -1
  176. package/dist/shell-check/index.d.ts +1 -1
  177. package/dist/shell-check/index.d.ts.map +1 -1
  178. package/dist/smart-rename/index.d.ts +1 -1
  179. package/dist/smart-rename/index.d.ts.map +1 -1
  180. package/dist/spec-linker/index.d.ts +1 -1
  181. package/dist/spec-linker/index.d.ts.map +1 -1
  182. package/dist/spec-linker.js +222 -20772
  183. package/dist/template-engine/index.d.ts +1 -1
  184. package/dist/template-engine/index.d.ts.map +1 -1
  185. package/dist/template-engine.js +34 -8
  186. package/dist/test-coverage-gate/index.d.ts +1 -1
  187. package/dist/test-coverage-gate/index.d.ts.map +1 -1
  188. package/dist/test-coverage-gate.js +34 -14
  189. package/dist/test-flake-detector/index.d.ts +1 -1
  190. package/dist/test-flake-detector/index.d.ts.map +1 -1
  191. package/dist/test-flake-detector.js +31 -20
  192. package/dist/test-generator/index.d.ts +1 -1
  193. package/dist/test-generator/index.d.ts.map +1 -1
  194. package/dist/test-runner-gate/index.d.ts +1 -1
  195. package/dist/test-runner-gate/index.d.ts.map +1 -1
  196. package/dist/test-runner-gate.js +132 -9
  197. package/dist/todo-listener/index.d.ts +1 -32
  198. package/dist/todo-listener/index.d.ts.map +1 -1
  199. package/dist/todo-listener.js +30 -1
  200. package/dist/todo-tracker/index.d.ts +1 -1
  201. package/dist/todo-tracker/index.d.ts.map +1 -1
  202. package/dist/todo-tracker.js +3 -4
  203. package/dist/token-budget/index.d.ts +1 -1
  204. package/dist/token-budget/index.d.ts.map +1 -1
  205. package/dist/token-throttle/index.d.ts +1 -1
  206. package/dist/token-throttle/index.d.ts.map +1 -1
  207. package/dist/token-throttle.js +17 -2
  208. package/dist/type-gate/index.d.ts +1 -1
  209. package/dist/type-gate/index.d.ts.map +1 -1
  210. package/dist/type-gate.js +106 -19
  211. package/package.json +80 -68
@@ -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,6 +153,7 @@ function hasDisabledPluginEntry(raw) {
51
153
  return name === "branch-guard" || name === "@wrongstack/plugins/branch-guard";
52
154
  });
53
155
  }
156
+ var branchCache = new BoundedMap({ max: 64, ttlMs: 3e4 });
54
157
  function runGit(args, cwd, signal) {
55
158
  return new Promise((resolve, reject) => {
56
159
  execFile(
@@ -65,11 +168,17 @@ function runGit(args, cwd, signal) {
65
168
  });
66
169
  }
67
170
  async function getCurrentBranch(cwd, signal) {
171
+ const cacheKey = `${cwd ?? "undefined"}`;
172
+ const cached = branchCache.get(cacheKey);
173
+ if (cached !== void 0) return cached;
68
174
  try {
69
175
  const branch = (await runGit(["branch", "--show-current"], cwd, signal)).trim();
70
- return branch || null;
176
+ const result = branch || null;
177
+ branchCache.set(cacheKey, result);
178
+ return result;
71
179
  } catch (err) {
72
180
  if (signal.aborted) throw err;
181
+ branchCache.set(cacheKey, null);
73
182
  return null;
74
183
  }
75
184
  }
@@ -157,6 +266,7 @@ var plugin = {
157
266
  state.hookUnregister = null;
158
267
  state.configUnregister = null;
159
268
  state.lastBlock = null;
269
+ branchCache.clear();
160
270
  let cfg = readHostConfig(api.config);
161
271
  state.configUnregister = api.onConfigChange((next) => {
162
272
  cfg = readHostConfig(next);
package/dist/catalog.d.ts CHANGED
@@ -1,39 +1,14 @@
1
1
  /**
2
- * Plugin catalog single source of truth for plugin names and their
3
- * canonical source paths. Used by `spec-linker` to detect unlinked
4
- * plugin references in markdown files.
5
- *
6
- * The catalog is built at module load time by importing each plugin
7
- * source and reading its `name` field. This means the catalog is
8
- * always in sync with the actual plugins — adding a new plugin
9
- * (exporting it from `./index.ts` AND adding a row to the table
10
- * below) is enough for `spec-linker` to start detecting references
11
- * to it.
12
- *
13
- * To add a new plugin: see the table in `catalog.ts`. The `name`
14
- * must match the plugin's `name` field; the `path` is the
15
- * relative source directory under `packages/plugins/src/`.
16
- *
17
- * @public
2
+ * Catalog projections used by spec-linker and public discovery APIs.
3
+ * Plugin identity and source paths are owned by the typed manifest.
18
4
  */
19
- import type { Plugin } from '@wrongstack/core';
20
- interface CatalogEntry {
21
- /** The plugin's `name` field. */
5
+ import type { Plugin } from '@wrongstack/core/types';
6
+ export interface CatalogEntry {
22
7
  name: string;
23
- /** Relative path under `packages/plugins/src/`, e.g. `./src/auto-doc`. */
24
8
  path: string;
25
9
  }
26
- /**
27
- * Read-only view of the catalog as a Map from plugin name to its
28
- * source path. Frozen so consumers cannot mutate it.
29
- */
30
- export declare const PLUGIN_CATALOG: ReadonlyMap<string, string>;
31
- /**
32
- * The list of catalog entries, ordered by the table above. Used by
33
- * `spec-linker` to iterate names in a stable order for detection.
34
- */
35
10
  export declare const PLUGIN_CATALOG_ENTRIES: readonly CatalogEntry[];
36
- /** Convenience accessor: just the names, in declaration order. */
11
+ export declare const PLUGIN_CATALOG: ReadonlyMap<string, string>;
37
12
  export declare const PLUGIN_NAMES: readonly string[];
38
13
  export type { Plugin };
39
14
  //# sourceMappingURL=catalog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAwE/C,UAAU,YAAY;IACpB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;CACd;AAgGD;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAInD,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,SAAS,YAAY,EAEzD,CAAC;AAEF,kEAAkE;AAClE,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAA8C,CAAC;AAIzF,YAAY,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGrD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,sBAAsB,EAAE,SAAS,YAAY,EAIzD,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAEtD,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAEzC,CAAC;AAEF,YAAY,EAAE,MAAM,EAAE,CAAC"}
@@ -43,7 +43,7 @@
43
43
  *
44
44
  * @public
45
45
  */
46
- import type { Plugin } from '@wrongstack/core';
46
+ import type { Plugin } from '@wrongstack/core/types';
47
47
  type Section = 'Added' | 'Changed' | 'Fixed' | 'Removed' | 'Security' | 'Documentation' | 'Maintenance';
48
48
  export interface ChangelogEntry {
49
49
  section: Section;
@@ -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,kBAAkB,CAAC;AAM/C,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 });
@@ -35,7 +35,7 @@
35
35
  *
36
36
  * @public
37
37
  */
38
- import type { Plugin } from '@wrongstack/core';
38
+ import type { Plugin } from '@wrongstack/core/types';
39
39
  export interface Snapshot {
40
40
  id: string;
41
41
  createdAt: string;
@@ -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;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAM/C,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;AA+HD,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"}
@@ -1,6 +1,5 @@
1
1
  // src/checkpoint/index.ts
2
- import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
3
- import { readFile, stat } from "node:fs/promises";
2
+ import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
4
3
  import { dirname, isAbsolute, relative, resolve } from "node:path";
5
4
  var state = {
6
5
  snapshots: [],
@@ -8,13 +7,15 @@ var state = {
8
7
  captures: 0,
9
8
  restores: 0,
10
9
  skippedLarge: 0,
10
+ evictedForBytes: 0,
11
11
  hookUnregister: null
12
12
  };
13
13
  var DEFAULTS = {
14
14
  enabled: true,
15
15
  autoCapture: true,
16
16
  maxSnapshots: 50,
17
- maxFileBytes: 1048576
17
+ maxFileBytes: 1048576,
18
+ maxTotalBytes: 64 * 1048576
18
19
  };
19
20
  function readConfig(raw) {
20
21
  if (!raw || typeof raw !== "object") return { ...DEFAULTS };
@@ -23,6 +24,7 @@ function readConfig(raw) {
23
24
  enabled: r["enabled"] !== false,
24
25
  autoCapture: r["autoCapture"] !== false,
25
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,
26
28
  maxFileBytes: typeof r["maxFileBytes"] === "number" && r["maxFileBytes"] >= 1024 ? r["maxFileBytes"] : DEFAULTS.maxFileBytes
27
29
  };
28
30
  }
@@ -41,11 +43,11 @@ function hashContent(s) {
41
43
  }
42
44
  return h >>> 0;
43
45
  }
44
- function captureFile(path, maxBytes) {
46
+ async function captureFile(path, maxBytes) {
45
47
  try {
46
- const st = statSync(path);
48
+ const st = await stat(path);
47
49
  if (st.size > maxBytes) return "too-large";
48
- const content = readFileSync(path, "utf-8");
50
+ const content = await readFile(path, "utf-8");
49
51
  return { path, content, bytes: st.size };
50
52
  } catch {
51
53
  return { path, content: null, bytes: 0 };
@@ -64,11 +66,22 @@ async function captureFileForHook(path, maxBytes, signal) {
64
66
  return { path, content: null, bytes: 0 };
65
67
  }
66
68
  }
67
- 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) {
68
77
  state.snapshots.push(snapshot);
69
78
  if (state.snapshots.length > maxSnapshots) {
70
79
  state.snapshots.splice(0, state.snapshots.length - maxSnapshots);
71
80
  }
81
+ while (state.snapshots.length > 1 && retainedSnapshotBytes() > maxTotalBytes) {
82
+ state.snapshots.shift();
83
+ state.evictedForBytes += 1;
84
+ }
72
85
  }
73
86
  var plugin = {
74
87
  name: "checkpoint",
@@ -107,6 +120,7 @@ var plugin = {
107
120
  state.captures = 0;
108
121
  state.restores = 0;
109
122
  state.skippedLarge = 0;
123
+ state.evictedForBytes = 0;
110
124
  if (state.hookUnregister) {
111
125
  try {
112
126
  state.hookUnregister();
@@ -134,7 +148,8 @@ var plugin = {
134
148
  origin: `auto:${input.toolName ?? "unknown"}`,
135
149
  files: [captured]
136
150
  },
137
- cfg.maxSnapshots
151
+ cfg.maxSnapshots,
152
+ cfg.maxTotalBytes
138
153
  );
139
154
  state.captures += 1;
140
155
  api.metrics.counter("captures");
@@ -188,7 +203,7 @@ var plugin = {
188
203
  rejectedOutsideProject.push(p);
189
204
  continue;
190
205
  }
191
- const captured = captureFile(safePath, cfg.maxFileBytes);
206
+ const captured = await captureFile(safePath, cfg.maxFileBytes);
192
207
  if (captured === "too-large") {
193
208
  skipped += 1;
194
209
  state.skippedLarge += 1;
@@ -212,7 +227,7 @@ var plugin = {
212
227
  origin: input.label?.trim() ? `manual:${input.label.trim()}` : "manual",
213
228
  files
214
229
  };
215
- pushSnapshot(snapshot, cfg.maxSnapshots);
230
+ pushSnapshot(snapshot, cfg.maxSnapshots, cfg.maxTotalBytes);
216
231
  state.captures += 1;
217
232
  api.metrics.counter("captures");
218
233
  return {
@@ -255,7 +270,9 @@ var plugin = {
255
270
  counters: {
256
271
  captures: state.captures,
257
272
  restores: state.restores,
258
- skippedLarge: state.skippedLarge
273
+ skippedLarge: state.skippedLarge,
274
+ retainedBytes: retainedSnapshotBytes(),
275
+ evictedForBytes: state.evictedForBytes
259
276
  }
260
277
  };
261
278
  }
@@ -298,8 +315,8 @@ var plugin = {
298
315
  continue;
299
316
  }
300
317
  try {
301
- mkdirSync(dirname(f.path), { recursive: true });
302
- writeFileSync(f.path, f.content);
318
+ await mkdir(dirname(f.path), { recursive: true });
319
+ await writeFile(f.path, f.content);
303
320
  restored.push(f.path);
304
321
  } catch (err) {
305
322
  errors.push({ path: f.path, error: err instanceof Error ? err.message : String(err) });
@@ -337,13 +354,16 @@ var plugin = {
337
354
  captures: state.captures,
338
355
  restores: state.restores,
339
356
  skippedLarge: state.skippedLarge,
340
- snapshotsHeld: state.snapshots.length
357
+ snapshotsHeld: state.snapshots.length,
358
+ retainedBytes: retainedSnapshotBytes(),
359
+ evictedForBytes: state.evictedForBytes
341
360
  };
342
361
  state.snapshots = [];
343
362
  state.nextId = 1;
344
363
  state.captures = 0;
345
364
  state.restores = 0;
346
365
  state.skippedLarge = 0;
366
+ state.evictedForBytes = 0;
347
367
  api.log.info("checkpoint: teardown complete", { final });
348
368
  },
349
369
  async health() {
@@ -354,12 +374,17 @@ var plugin = {
354
374
  snapshotsHeld: state.snapshots.length,
355
375
  captures: state.captures,
356
376
  restores: state.restores,
357
- 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
358
382
  }
359
383
  };
360
384
  }
361
385
  };
362
386
  var checkpoint_default = plugin;
363
387
  export {
364
- checkpoint_default as default
388
+ checkpoint_default as default,
389
+ retainedSnapshotBytes
365
390
  };
@@ -23,7 +23,7 @@
23
23
  *
24
24
  * @public
25
25
  */
26
- import type { Plugin } from '@wrongstack/core';
26
+ import type { Plugin } from '@wrongstack/core/types';
27
27
  declare const plugin: Plugin;
28
28
  export default plugin;
29
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/code-metrics/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA0M/C,QAAA,MAAM,MAAM,EAAE,MAgMb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/code-metrics/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA6MrD,QAAA,MAAM,MAAM,EAAE,MAgMb,CAAC;eAEa,MAAM"}