@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
@@ -7,7 +7,7 @@
7
7
  * - template_create: Save a named template to the plugin store
8
8
  * - template_list: List all saved templates
9
9
  */
10
- import type { Plugin } from '@wrongstack/core';
10
+ import type { Plugin } from '@wrongstack/core/types';
11
11
  declare const plugin: Plugin;
12
12
  export default plugin;
13
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/template-engine/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAqH/C,QAAA,MAAM,MAAM,EAAE,MA+Sb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/template-engine/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA+HrD,QAAA,MAAM,MAAM,EAAE,MA4Sb,CAAC;eAEa,MAAM"}
@@ -1,5 +1,31 @@
1
1
  // src/template-engine/index.ts
2
- import { isAbsolute } from "node:path";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { isAbsolute as isAbsolute2 } from "node:path";
4
+
5
+ // src/runtime/index.ts
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
12
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
13
+ function hasLeadingDash(arg) {
14
+ return arg.length > 0 && arg.startsWith("-");
15
+ }
16
+ function withinProjectPath(projectRoot, candidate) {
17
+ if (candidate.length === 0 || candidate.length > 4096) return false;
18
+ if (hasLeadingDash(candidate)) return false;
19
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
20
+ const rel = relative(projectRoot, resolved);
21
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
22
+ }
23
+ function withinProject(p) {
24
+ const cwd = process.cwd();
25
+ return withinProjectPath(cwd, p) || relative(cwd, p) === ".";
26
+ }
27
+
28
+ // src/template-engine/index.ts
3
29
  var API_VERSION = "^0.1.10";
4
30
  var templates = /* @__PURE__ */ new Map();
5
31
  function expandTemplate(template, variables) {
@@ -46,9 +72,12 @@ function renderTemplateRaw(template, variables) {
46
72
  return result;
47
73
  }
48
74
  function validateRelativeTemplatePath(field, value) {
49
- if (isAbsolute(value) || value.split(/[\\/]+/).includes("..")) {
75
+ if (isAbsolute2(value) || value.split(/[\\/]+/).includes("..")) {
50
76
  return `${field} must be a relative path without ".." components`;
51
77
  }
78
+ if (!withinProject(value)) {
79
+ return `${field} must resolve inside the project directory`;
80
+ }
52
81
  return null;
53
82
  }
54
83
  var plugin = {
@@ -119,8 +148,7 @@ var plugin = {
119
148
  if (output_path) {
120
149
  const pathError = validateRelativeTemplatePath("output_path", output_path);
121
150
  if (pathError) return { ok: false, error: pathError };
122
- const { writeFileSync } = await import("node:fs");
123
- writeFileSync(output_path, result, "utf-8");
151
+ await writeFile(output_path, result, "utf-8");
124
152
  return {
125
153
  ok: true,
126
154
  output_path,
@@ -173,8 +201,7 @@ var plugin = {
173
201
  }
174
202
  let content;
175
203
  try {
176
- const { readFileSync } = await import("node:fs");
177
- content = readFileSync(template_path, "utf-8");
204
+ content = await readFile(template_path, "utf-8");
178
205
  } catch (err) {
179
206
  return { ok: false, error: `Could not read template file: ${err}` };
180
207
  }
@@ -187,8 +214,7 @@ var plugin = {
187
214
  if (output_path) {
188
215
  const pathError = validateRelativeTemplatePath("output_path", output_path);
189
216
  if (pathError) return { ok: false, error: pathError };
190
- const { writeFileSync } = await import("node:fs");
191
- writeFileSync(output_path, result, "utf-8");
217
+ await writeFile(output_path, result, "utf-8");
192
218
  return {
193
219
  ok: true,
194
220
  template_path,
@@ -29,7 +29,7 @@
29
29
  *
30
30
  * @public
31
31
  */
32
- import type { Plugin } from '@wrongstack/core';
32
+ import type { Plugin } from '@wrongstack/core/types';
33
33
  declare const plugin: Plugin;
34
34
  export default plugin;
35
35
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-coverage-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAkI/C,QAAA,MAAM,MAAM,EAAE,MAyOb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-coverage-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAwIrD,QAAA,MAAM,MAAM,EAAE,MA0Ob,CAAC;eAEa,MAAM"}
@@ -1,6 +1,30 @@
1
1
  // src/test-coverage-gate/index.ts
2
2
  import { readFileSync } from "node:fs";
3
- import { isAbsolute, relative, resolve } from "node:path";
3
+
4
+ // src/runtime/index.ts
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
11
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
12
+ function hasLeadingDash(arg) {
13
+ return arg.length > 0 && arg.startsWith("-");
14
+ }
15
+ function withinProjectPath(projectRoot, candidate) {
16
+ if (candidate.length === 0 || candidate.length > 4096) return false;
17
+ if (hasLeadingDash(candidate)) return false;
18
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
19
+ const rel = relative(projectRoot, resolved);
20
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
21
+ }
22
+ function withinProject(p) {
23
+ const cwd = process.cwd();
24
+ return withinProjectPath(cwd, p) || relative(cwd, p) === ".";
25
+ }
26
+
27
+ // src/test-coverage-gate/index.ts
4
28
  var API_VERSION = "^0.1.10";
5
29
  var state = {
6
30
  invocationCount: 0,
@@ -21,28 +45,24 @@ var DEFAULTS = {
21
45
  deltaThreshold: 1,
22
46
  runOnChange: [".ts", ".tsx", ".js", ".jsx"]
23
47
  };
48
+ var runOnChangeSet = new Set(DEFAULTS.runOnChange);
24
49
  function readConfig(raw) {
25
- if (!raw || typeof raw !== "object") return { ...DEFAULTS };
50
+ if (!raw || typeof raw !== "object") {
51
+ runOnChangeSet = new Set(DEFAULTS.runOnChange);
52
+ return { ...DEFAULTS };
53
+ }
26
54
  const r = raw;
55
+ const runOnChange = Array.isArray(r["runOnChange"]) ? r["runOnChange"].filter((x) => typeof x === "string") : DEFAULTS.runOnChange;
56
+ runOnChangeSet = new Set(runOnChange);
27
57
  return {
28
58
  enabled: r["enabled"] !== false,
29
59
  coveragePath: typeof r["coveragePath"] === "string" ? r["coveragePath"] : DEFAULTS.coveragePath,
30
60
  threshold: typeof r["threshold"] === "number" && r["threshold"] >= 0 && r["threshold"] <= 100 ? r["threshold"] : DEFAULTS.threshold,
31
61
  mode: r["mode"] === "block" ? "block" : DEFAULTS.mode,
32
62
  deltaThreshold: typeof r["deltaThreshold"] === "number" && r["deltaThreshold"] >= 0 ? r["deltaThreshold"] : DEFAULTS.deltaThreshold,
33
- runOnChange: Array.isArray(r["runOnChange"]) ? r["runOnChange"].filter((x) => typeof x === "string") : DEFAULTS.runOnChange
63
+ runOnChange
34
64
  };
35
65
  }
36
- function withinProject(p) {
37
- if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
38
- const root = process.cwd();
39
- const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
40
- const rel = relative(root, resolved);
41
- if (rel === "" || rel === ".") return true;
42
- if (rel.startsWith("..")) return false;
43
- if (isAbsolute(rel)) return false;
44
- return true;
45
- }
46
66
  function readCoverageSummary(coveragePath) {
47
67
  try {
48
68
  const raw = readFileSync(coveragePath, "utf-8");
@@ -125,7 +145,7 @@ var plugin = {
125
145
  if (!sourcePath || typeof sourcePath !== "string") return;
126
146
  if (!withinProject(sourcePath)) return;
127
147
  const ext = sourcePath.includes(".") ? sourcePath.slice(sourcePath.lastIndexOf(".")).toLowerCase() : "";
128
- if (!cfg.runOnChange.includes(ext)) {
148
+ if (!runOnChangeSet.has(ext)) {
129
149
  state.skippedCount += 1;
130
150
  return;
131
151
  }
@@ -19,7 +19,7 @@
19
19
  *
20
20
  * @public
21
21
  */
22
- import type { Plugin } from '@wrongstack/core';
22
+ import type { Plugin } from '@wrongstack/core/types';
23
23
  declare const plugin: Plugin;
24
24
  export default plugin;
25
25
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-flake-detector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA2P/C,QAAA,MAAM,MAAM,EAAE,MA6Nb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-flake-detector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAyQrD,QAAA,MAAM,MAAM,EAAE,MA6Nb,CAAC;eAEa,MAAM"}
@@ -1,5 +1,5 @@
1
1
  // src/test-flake-detector/index.ts
2
- import { execFileSync } from "node:child_process";
2
+ import { execFile } from "node:child_process";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { createRequire } from "node:module";
5
5
  import { dirname, isAbsolute, relative, resolve } from "node:path";
@@ -144,24 +144,35 @@ function resolveTestCommand(baseCommand, testPattern) {
144
144
  };
145
145
  }
146
146
  function runOnce(command, timeoutMs) {
147
- try {
148
- const output = execFileSync(command.cmd, command.args, {
149
- encoding: "utf-8",
150
- timeout: timeoutMs,
151
- cwd: process.cwd(),
152
- stdio: ["pipe", "pipe", "pipe"],
153
- shell: false
154
- });
155
- return { output };
156
- } catch (err) {
157
- const e = err;
158
- if (e.killed) {
159
- return { output: e.stdout ?? "", error: `timeout after ${timeoutMs}ms` };
160
- }
161
- const output = `${e.stdout ?? ""}
162
- ${e.stderr ?? ""}`.trim();
163
- return { output, error: e.message ?? "test command exited with non-zero code" };
164
- }
147
+ return new Promise((resolveRun) => {
148
+ execFile(
149
+ command.cmd,
150
+ command.args,
151
+ {
152
+ encoding: "utf8",
153
+ timeout: timeoutMs,
154
+ cwd: process.cwd(),
155
+ windowsHide: true,
156
+ maxBuffer: 10 * 1024 * 1024,
157
+ shell: false
158
+ },
159
+ (error, stdout, stderr) => {
160
+ if (!error) {
161
+ resolveRun({ output: stdout });
162
+ return;
163
+ }
164
+ if (error.killed) {
165
+ resolveRun({ output: stdout, error: `timeout after ${timeoutMs}ms` });
166
+ return;
167
+ }
168
+ resolveRun({
169
+ output: `${stdout}
170
+ ${stderr}`.trim(),
171
+ error: error.message || "test command exited with non-zero code"
172
+ });
173
+ }
174
+ );
175
+ });
165
176
  }
166
177
  var plugin = {
167
178
  name: "test-flake-detector",
@@ -251,7 +262,7 @@ var plugin = {
251
262
  let runsCompleted = 0;
252
263
  const runErrors = [];
253
264
  for (let run = 1; run <= requestedRuns; run += 1) {
254
- const { output, error } = runOnce(command, cfg.timeoutMs);
265
+ const { output, error } = await runOnce(command, cfg.timeoutMs);
255
266
  state.runCount += 1;
256
267
  if (error) {
257
268
  state.errorCount += 1;
@@ -22,7 +22,7 @@
22
22
  *
23
23
  * @public
24
24
  */
25
- import type { Plugin } from '@wrongstack/core';
25
+ import type { Plugin } from '@wrongstack/core/types';
26
26
  export interface DetectedExport {
27
27
  name: string;
28
28
  kind: 'function' | 'arrow' | 'class' | 'named';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-generator/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAkG/C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CAChD;AAkLD,QAAA,MAAM,MAAM,EAAE,MAqLb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-generator/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAkGrD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;CAChD;AAkLD,QAAA,MAAM,MAAM,EAAE,MAqLb,CAAC;eAEa,MAAM"}
@@ -29,7 +29,7 @@
29
29
  *
30
30
  * @public
31
31
  */
32
- import type { Plugin } from '@wrongstack/core';
32
+ import type { Plugin } from '@wrongstack/core/types';
33
33
  declare const plugin: Plugin;
34
34
  export default plugin;
35
35
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-runner-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAsc/C,QAAA,MAAM,MAAM,EAAE,MA+Tb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-runner-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAufrD,QAAA,MAAM,MAAM,EAAE,MA+Tb,CAAC;eAEa,MAAM"}
@@ -2,9 +2,109 @@
2
2
  import { execFile } from "node:child_process";
3
3
  import { access } from "node:fs/promises";
4
4
  import { basename as basename2, dirname, isAbsolute as isAbsolute2, join } from "node:path";
5
+ import { buildWin32CmdShimInvocation as buildWin32CmdShimInvocation2, resolveWin32Command as resolveWin32Command2 } from "@wrongstack/tools/win32";
5
6
 
6
7
  // src/runtime/index.ts
7
8
  import { basename, extname, isAbsolute, relative, resolve } from "node:path";
9
+
10
+ // src/runtime/local-bin.ts
11
+ import { buildWin32CmdShimInvocation, resolveWin32Command } from "@wrongstack/tools/win32";
12
+
13
+ // src/runtime/bounded-map.ts
14
+ var BoundedMap = class {
15
+ map = /* @__PURE__ */ new Map();
16
+ max;
17
+ ttlMs;
18
+ now;
19
+ /** Entries dropped to stay under `max`. Surfaced by plugin health(). */
20
+ evictions = 0;
21
+ constructor(options) {
22
+ const normalizedMax = Math.floor(options.max);
23
+ this.max = Number.isSafeInteger(normalizedMax) ? Math.max(1, normalizedMax) : 1;
24
+ this.ttlMs = options.ttlMs;
25
+ this.now = options.now ?? Date.now;
26
+ }
27
+ expired(entry) {
28
+ return this.ttlMs !== void 0 && this.now() - entry.storedAt > this.ttlMs;
29
+ }
30
+ get(key) {
31
+ const entry = this.map.get(key);
32
+ if (entry === void 0) return void 0;
33
+ if (this.expired(entry)) {
34
+ this.map.delete(key);
35
+ return void 0;
36
+ }
37
+ this.map.delete(key);
38
+ this.map.set(key, entry);
39
+ return entry.value;
40
+ }
41
+ /**
42
+ * Read without promoting the key to most-recently-used. Use for
43
+ * diagnostics that must not perturb the eviction order.
44
+ */
45
+ peek(key) {
46
+ const entry = this.map.get(key);
47
+ if (entry === void 0 || this.expired(entry)) return void 0;
48
+ return entry.value;
49
+ }
50
+ has(key) {
51
+ const entry = this.map.get(key);
52
+ if (entry === void 0) return false;
53
+ if (this.expired(entry)) {
54
+ this.map.delete(key);
55
+ return false;
56
+ }
57
+ return true;
58
+ }
59
+ set(key, value) {
60
+ this.map.delete(key);
61
+ this.map.set(key, { value, storedAt: this.now() });
62
+ while (this.map.size > this.max) {
63
+ const coldest = this.map.keys().next().value;
64
+ if (coldest === void 0) break;
65
+ this.map.delete(coldest);
66
+ this.evictions += 1;
67
+ }
68
+ return this;
69
+ }
70
+ delete(key) {
71
+ return this.map.delete(key);
72
+ }
73
+ clear() {
74
+ this.map.clear();
75
+ this.evictions = 0;
76
+ }
77
+ get size() {
78
+ return this.map.size;
79
+ }
80
+ /** How many entries have been dropped to respect `max`, since the last clear. */
81
+ get evictionCount() {
82
+ return this.evictions;
83
+ }
84
+ /** Drop every expired entry. Cheap enough to call from a status tool. */
85
+ prune() {
86
+ if (this.ttlMs === void 0) return 0;
87
+ let removed = 0;
88
+ for (const [key, entry] of this.map) {
89
+ if (this.expired(entry)) {
90
+ this.map.delete(key);
91
+ removed += 1;
92
+ }
93
+ }
94
+ return removed;
95
+ }
96
+ /** Live (non-expired) entries, coldest first. */
97
+ *entries() {
98
+ for (const [key, entry] of this.map) {
99
+ if (!this.expired(entry)) yield [key, entry.value];
100
+ }
101
+ }
102
+ [Symbol.iterator]() {
103
+ return this.entries();
104
+ }
105
+ };
106
+
107
+ // src/runtime/index.ts
8
108
  var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
9
109
  function hasLeadingDash(arg) {
10
110
  return arg.length > 0 && arg.startsWith("-");
@@ -17,10 +117,20 @@ function withinProjectPath(projectRoot, candidate) {
17
117
  return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
18
118
  }
19
119
  function withinProject(p) {
20
- return withinProjectPath(process.cwd(), p) || relative(process.cwd(), p) === ".";
120
+ const cwd = process.cwd();
121
+ return withinProjectPath(cwd, p) || relative(cwd, p) === ".";
21
122
  }
22
123
 
23
124
  // src/test-runner-gate/index.ts
125
+ function resolveExec(command, args) {
126
+ const resolved = resolveWin32Command2(command);
127
+ const needsShell = process.platform === "win32" && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
128
+ if (needsShell) {
129
+ const shim = buildWin32CmdShimInvocation2(resolved, args);
130
+ return { cmd: shim.command, args: shim.args, windowsVerbatimArguments: true };
131
+ }
132
+ return { cmd: resolved, args: [...args], windowsVerbatimArguments: false };
133
+ }
24
134
  var API_VERSION = "^0.1.10";
25
135
  var state = {
26
136
  invocationCount: 0,
@@ -85,7 +195,7 @@ function pathContentHash(content) {
85
195
  }
86
196
  return h >>> 0;
87
197
  }
88
- var lastPassedHash = /* @__PURE__ */ new Map();
198
+ var lastPassedHash = new BoundedMap({ max: 1024 });
89
199
  var ALLOWED_COMMAND_TOKENS = /* @__PURE__ */ new Set([
90
200
  "npx",
91
201
  "pnpm",
@@ -140,10 +250,13 @@ async function detectRunner(requested) {
140
250
  if (!match) return null;
141
251
  try {
142
252
  await new Promise((resolve2, reject) => {
143
- execFile("npx", [`${match.name}`, "--version"], {
253
+ const ex = resolveExec("npx", [`${match.name}`, "--version"]);
254
+ execFile(ex.cmd, ex.args, {
144
255
  encoding: "utf-8",
145
256
  timeout: 5e3,
146
- cwd: process.cwd()
257
+ cwd: process.cwd(),
258
+ windowsHide: true,
259
+ ...ex.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
147
260
  }, (err) => err ? reject(err) : resolve2());
148
261
  });
149
262
  return match;
@@ -154,10 +267,13 @@ async function detectRunner(requested) {
154
267
  for (const candidate of candidates) {
155
268
  try {
156
269
  await new Promise((resolve2, reject) => {
157
- execFile("npx", [`${candidate.name}`, "--version"], {
270
+ const ex = resolveExec("npx", [`${candidate.name}`, "--version"]);
271
+ execFile(ex.cmd, ex.args, {
158
272
  encoding: "utf-8",
159
273
  timeout: 5e3,
160
- cwd: process.cwd()
274
+ cwd: process.cwd(),
275
+ windowsHide: true,
276
+ ...ex.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
161
277
  }, (err) => err ? reject(err) : resolve2());
162
278
  });
163
279
  return candidate;
@@ -205,10 +321,17 @@ async function runTests(testFile, runner, customCommand, timeoutMs) {
205
321
  try {
206
322
  const { stdout: out } = await new Promise(
207
323
  (resolve2, reject) => {
324
+ const ex = resolveExec(cmd, fullArgs);
208
325
  execFile(
209
- cmd,
210
- fullArgs,
211
- { encoding: "utf-8", timeout: timeoutMs, cwd: process.cwd() },
326
+ ex.cmd,
327
+ ex.args,
328
+ {
329
+ encoding: "utf-8",
330
+ timeout: timeoutMs,
331
+ cwd: process.cwd(),
332
+ windowsHide: true,
333
+ ...ex.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}
334
+ },
212
335
  (err, out2, stderr) => {
213
336
  if (err) reject(Object.assign(err, { stdout: out2, stderr }));
214
337
  else resolve2({ stdout: out2, stderr });
@@ -1,35 +1,4 @@
1
- /**
2
- * todo-listener plugin — PostToolUse hook on the `todo` tool that
3
- * broadcasts a structured status update to the project mailbox.
4
- *
5
- * When the agent (or any plugin) calls the built-in `todo` tool, the
6
- * full todo list is replaced in `ctx.todos`. The hook fires after the
7
- * tool completes, reads the new state, and posts a compact summary
8
- * to the project mailbox so that other agents in the same project
9
- * (terminals, WebUIs, shadow agents) can see what this agent is
10
- * working on in real time.
11
- *
12
- * Use cases:
13
- * - Multi-agent fleets where a coordinator should know which
14
- * sub-agent is working on which item
15
- * - Long-running sessions where a user opens a second terminal and
16
- * wants to see the in-progress plan
17
- * - Shadow agents that audit progress across the project
18
- *
19
- * Config (`config.extensions['todo-listener']`):
20
- *
21
- * ```jsonc
22
- * {
23
- * "enabled": false,
24
- * "subjectPrefix": "todo: ",
25
- * "broadcastOnChange": true,
26
- * "cooldownMs": 30000
27
- * }
28
- * ```
29
- *
30
- * @public
31
- */
32
- import type { Plugin } from '@wrongstack/core';
1
+ import type { Plugin } from '@wrongstack/core/types';
33
2
  declare const plugin: Plugin;
34
3
  export default plugin;
35
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/todo-listener/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,MAAM,EAAY,MAAM,kBAAkB,CAAC;AAoGzD,QAAA,MAAM,MAAM,EAAE,MAqNb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/todo-listener/index.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAoGrD,QAAA,MAAM,MAAM,EAAE,MAuNb,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/todo-listener/index.ts
2
31
  var state = {
3
32
  invocationCount: 0,
@@ -112,7 +141,7 @@ var plugin = {
112
141
  0,
113
142
  200
114
143
  );
115
- const body = JSON.stringify(payload, null, 2);
144
+ const body = safeJsonStringify(payload, 2);
116
145
  const sendInput = {
117
146
  from: `plugin:todo-listener`,
118
147
  to: "*",
@@ -1,4 +1,4 @@
1
- import type { Plugin } from '@wrongstack/core';
1
+ import type { Plugin } from '@wrongstack/core/types';
2
2
  declare const plugin: Plugin;
3
3
  export default plugin;
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/todo-tracker/index.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAmK/C,QAAA,MAAM,MAAM,EAAE,MAsZb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/todo-tracker/index.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAuKrD,QAAA,MAAM,MAAM,EAAE,MAsZb,CAAC;eAEa,MAAM"}
@@ -1,6 +1,7 @@
1
1
  // src/todo-tracker/index.ts
2
2
  import * as fsp from "node:fs/promises";
3
3
  import { randomUUID } from "node:crypto";
4
+ import { atomicWrite, ensureDir } from "@wrongstack/core/utils";
4
5
  function deriveFilePath(api) {
5
6
  const raw = api.config.extensions?.["todo-tracker"];
6
7
  const explicit = typeof raw?.["filePath"] === "string" ? raw["filePath"] : null;
@@ -30,10 +31,8 @@ async function loadFile(filePath) {
30
31
  }
31
32
  }
32
33
  async function saveFile(filePath, file) {
33
- const tmp = `${filePath}.${randomUUID().slice(0, 8)}.tmp`;
34
- await fsp.mkdir(filePath.replace(/[/\\][^/\\]+$/, ""), { recursive: true });
35
- await fsp.writeFile(tmp, JSON.stringify(file, null, 2), { encoding: "utf8", mode: 384 });
36
- await fsp.rename(tmp, filePath);
34
+ await ensureDir(filePath.replace(/[/\\][^/\\]+$/, ""));
35
+ await atomicWrite(filePath, JSON.stringify(file, null, 2), { mode: 384 });
37
36
  }
38
37
  var state = {
39
38
  filePath: null,
@@ -32,7 +32,7 @@
32
32
  *
33
33
  * @public
34
34
  */
35
- import type { Plugin } from '@wrongstack/core';
35
+ import type { Plugin } from '@wrongstack/core/types';
36
36
  declare const plugin: Plugin;
37
37
  export default plugin;
38
38
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/token-budget/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAqF/C,QAAA,MAAM,MAAM,EAAE,MA8Qb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/token-budget/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAqFrD,QAAA,MAAM,MAAM,EAAE,MA8Qb,CAAC;eAEa,MAAM"}
@@ -31,7 +31,7 @@
31
31
  *
32
32
  * @public
33
33
  */
34
- import type { Plugin } from '@wrongstack/core';
34
+ import type { Plugin } from '@wrongstack/core/types';
35
35
  export interface SpendEntry {
36
36
  at: number;
37
37
  tokens: number;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/token-throttle/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,MAAM,EAAa,MAAM,kBAAkB,CAAC;AA8C1D,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAE5E;AAED,0CAA0C;AAC1C,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,UAAU,EAAE,EACrB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,CAoBR;AA4CD,QAAA,MAAM,MAAM,EAAE,MAoKb,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/token-throttle/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,MAAM,EAAa,MAAM,wBAAwB,CAAC;AA8ChE,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,CAE5E;AAED,0CAA0C;AAC1C,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,MAAM,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,UAAU,EAAE,EACrB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,CAoBR;AAyED,QAAA,MAAM,MAAM,EAAE,MAqKb,CAAC;eAEa,MAAM"}