@wrongstack/plugins 0.284.0 → 0.285.0

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 (263) hide show
  1. package/README.md +18 -14
  2. package/dist/accessibility-auditor/index.d.ts +38 -0
  3. package/dist/accessibility-auditor/index.d.ts.map +1 -0
  4. package/dist/accessibility-auditor.d.ts +2 -40
  5. package/dist/accessibility-auditor.js +5 -5
  6. package/dist/agent-handoff/index.d.ts +35 -0
  7. package/dist/agent-handoff/index.d.ts.map +1 -0
  8. package/dist/agent-handoff.d.ts +2 -37
  9. package/dist/agent-handoff.js +3 -2
  10. package/dist/api-compatibility-gate/index.d.ts +36 -0
  11. package/dist/api-compatibility-gate/index.d.ts.map +1 -0
  12. package/dist/api-compatibility-gate.d.ts +2 -38
  13. package/dist/api-compatibility-gate.js +6 -6
  14. package/dist/auto-doc/index.d.ts +20 -0
  15. package/dist/auto-doc/index.d.ts.map +1 -0
  16. package/dist/auto-doc.d.ts +2 -22
  17. package/dist/auto-doc.js +5 -5
  18. package/dist/auto-escalate/index.d.ts +43 -0
  19. package/dist/auto-escalate/index.d.ts.map +1 -0
  20. package/dist/auto-escalate.d.ts +2 -45
  21. package/dist/auto-escalate.js +5 -2
  22. package/dist/auto-i18n-extractor/index.d.ts +34 -0
  23. package/dist/auto-i18n-extractor/index.d.ts.map +1 -0
  24. package/dist/auto-i18n-extractor.d.ts +2 -36
  25. package/dist/auto-i18n-extractor.js +5 -5
  26. package/dist/branch-guard/index.d.ts +32 -0
  27. package/dist/branch-guard/index.d.ts.map +1 -0
  28. package/dist/branch-guard.d.ts +2 -34
  29. package/dist/branch-guard.js +4 -4
  30. package/dist/catalog.d.ts +39 -0
  31. package/dist/catalog.d.ts.map +1 -0
  32. package/dist/changelog-writer/index.d.ts +71 -0
  33. package/dist/changelog-writer/index.d.ts.map +1 -0
  34. package/dist/changelog-writer.d.ts +2 -73
  35. package/dist/changelog-writer.js +9 -5
  36. package/dist/checkpoint/index.d.ts +53 -0
  37. package/dist/checkpoint/index.d.ts.map +1 -0
  38. package/dist/checkpoint.d.ts +2 -55
  39. package/dist/checkpoint.js +6 -6
  40. package/dist/code-metrics/index.d.ts +29 -0
  41. package/dist/code-metrics/index.d.ts.map +1 -0
  42. package/dist/code-metrics.d.ts +2 -31
  43. package/dist/code-metrics.js +5 -5
  44. package/dist/commit-validator/index.d.ts +31 -0
  45. package/dist/commit-validator/index.d.ts.map +1 -0
  46. package/dist/commit-validator.d.ts +2 -33
  47. package/dist/commit-validator.js +5 -3
  48. package/dist/config-validator/index.d.ts +46 -0
  49. package/dist/config-validator/index.d.ts.map +1 -0
  50. package/dist/config-validator.d.ts +2 -48
  51. package/dist/config-validator.js +8 -4
  52. package/dist/context-pins/index.d.ts +43 -0
  53. package/dist/context-pins/index.d.ts.map +1 -0
  54. package/dist/context-pins.d.ts +2 -45
  55. package/dist/context-pins.js +5 -5
  56. package/dist/cost-tracker/index.d.ts +51 -0
  57. package/dist/cost-tracker/index.d.ts.map +1 -0
  58. package/dist/cost-tracker.d.ts +2 -53
  59. package/dist/cost-tracker.js +4 -4
  60. package/dist/cron/index.d.ts +12 -0
  61. package/dist/cron/index.d.ts.map +1 -0
  62. package/dist/cron.d.ts +2 -14
  63. package/dist/cron.js +3 -2
  64. package/dist/dead-code-detector/index.d.ts +32 -0
  65. package/dist/dead-code-detector/index.d.ts.map +1 -0
  66. package/dist/dead-code-detector.d.ts +2 -34
  67. package/dist/dead-code-detector.js +5 -5
  68. package/dist/dep-guard/index.d.ts +63 -0
  69. package/dist/dep-guard/index.d.ts.map +1 -0
  70. package/dist/dep-guard.d.ts +2 -65
  71. package/dist/dep-guard.js +6 -2
  72. package/dist/dependency-vulnerability-gate/index.d.ts +33 -0
  73. package/dist/dependency-vulnerability-gate/index.d.ts.map +1 -0
  74. package/dist/dependency-vulnerability-gate.d.ts +2 -35
  75. package/dist/dependency-vulnerability-gate.js +5 -5
  76. package/dist/diff-summary/index.d.ts +34 -0
  77. package/dist/diff-summary/index.d.ts.map +1 -0
  78. package/dist/diff-summary.d.ts +2 -36
  79. package/dist/diff-summary.js +5 -5
  80. package/dist/doc-sync-guard/index.d.ts +31 -0
  81. package/dist/doc-sync-guard/index.d.ts.map +1 -0
  82. package/dist/doc-sync-guard.d.ts +2 -33
  83. package/dist/doc-sync-guard.js +4 -4
  84. package/dist/duplicate-code-detector/index.d.ts +31 -0
  85. package/dist/duplicate-code-detector/index.d.ts.map +1 -0
  86. package/dist/duplicate-code-detector.d.ts +2 -33
  87. package/dist/duplicate-code-detector.js +5 -5
  88. package/dist/error-lens/index.d.ts +65 -0
  89. package/dist/error-lens/index.d.ts.map +1 -0
  90. package/dist/error-lens.d.ts +2 -67
  91. package/dist/error-lens.js +5 -2
  92. package/dist/feature-flag-tracker/index.d.ts +36 -0
  93. package/dist/feature-flag-tracker/index.d.ts.map +1 -0
  94. package/dist/feature-flag-tracker.d.ts +2 -38
  95. package/dist/feature-flag-tracker.js +5 -5
  96. package/dist/file-watcher/index.d.ts +12 -0
  97. package/dist/file-watcher/index.d.ts.map +1 -0
  98. package/dist/file-watcher.d.ts +2 -14
  99. package/dist/file-watcher.js +7 -7
  100. package/dist/format-on-save/index.d.ts +33 -0
  101. package/dist/format-on-save/index.d.ts.map +1 -0
  102. package/dist/format-on-save.d.ts +2 -35
  103. package/dist/format-on-save.js +6 -6
  104. package/dist/git-autocommit/index.d.ts +15 -0
  105. package/dist/git-autocommit/index.d.ts.map +1 -0
  106. package/dist/git-autocommit.d.ts +2 -17
  107. package/dist/git-autocommit.js +5 -5
  108. package/dist/import-organizer/index.d.ts +50 -0
  109. package/dist/import-organizer/index.d.ts.map +1 -0
  110. package/dist/import-organizer.d.ts +2 -52
  111. package/dist/import-organizer.js +6 -6
  112. package/dist/index.d.ts +186 -64
  113. package/dist/index.d.ts.map +1 -0
  114. package/dist/index.js +946 -394
  115. package/dist/injection-shield/index.d.ts +47 -0
  116. package/dist/injection-shield/index.d.ts.map +1 -0
  117. package/dist/injection-shield.d.ts +2 -49
  118. package/dist/injection-shield.js +4 -2
  119. package/dist/interface-contract-guard/index.d.ts +35 -0
  120. package/dist/interface-contract-guard/index.d.ts.map +1 -0
  121. package/dist/interface-contract-guard.d.ts +2 -37
  122. package/dist/interface-contract-guard.js +5 -5
  123. package/dist/knowledge-graph/index.d.ts +43 -0
  124. package/dist/knowledge-graph/index.d.ts.map +1 -0
  125. package/dist/knowledge-graph.d.ts +2 -45
  126. package/dist/knowledge-graph.js +5 -5
  127. package/dist/license-audit-gate/index.d.ts +32 -0
  128. package/dist/license-audit-gate/index.d.ts.map +1 -0
  129. package/dist/license-audit-gate.d.ts +2 -34
  130. package/dist/license-audit-gate.js +5 -5
  131. package/dist/lint-gate/index.d.ts +31 -0
  132. package/dist/lint-gate/index.d.ts.map +1 -0
  133. package/dist/lint-gate.d.ts +2 -33
  134. package/dist/lint-gate.js +7 -7
  135. package/dist/llm-cache/index.d.ts +54 -0
  136. package/dist/llm-cache/index.d.ts.map +1 -0
  137. package/dist/llm-cache.d.ts +2 -56
  138. package/dist/llm-cache.js +6 -4
  139. package/dist/loop-breaker/index.d.ts +4 -0
  140. package/dist/loop-breaker/index.d.ts.map +1 -0
  141. package/dist/loop-breaker.d.ts +2 -5
  142. package/dist/loop-breaker.js +4 -4
  143. package/dist/migration-planner/index.d.ts +28 -0
  144. package/dist/migration-planner/index.d.ts.map +1 -0
  145. package/dist/migration-planner.d.ts +2 -30
  146. package/dist/migration-planner.js +5 -5
  147. package/dist/model-router/index.d.ts +67 -0
  148. package/dist/model-router/index.d.ts.map +1 -0
  149. package/dist/model-router.d.ts +2 -69
  150. package/dist/model-router.js +5 -2
  151. package/dist/notify-hub/index.d.ts +43 -0
  152. package/dist/notify-hub/index.d.ts.map +1 -0
  153. package/dist/notify-hub.d.ts +2 -45
  154. package/dist/notify-hub.js +3 -2
  155. package/dist/path-guard/index.d.ts +52 -0
  156. package/dist/path-guard/index.d.ts.map +1 -0
  157. package/dist/path-guard.d.ts +2 -54
  158. package/dist/path-guard.js +5 -2
  159. package/dist/performance-regression-gate/index.d.ts +31 -0
  160. package/dist/performance-regression-gate/index.d.ts.map +1 -0
  161. package/dist/performance-regression-gate.d.ts +2 -33
  162. package/dist/performance-regression-gate.js +5 -5
  163. package/dist/plugin-stack-observer/index.d.ts +33 -0
  164. package/dist/plugin-stack-observer/index.d.ts.map +1 -0
  165. package/dist/plugin-stack-observer.d.ts +2 -35
  166. package/dist/plugin-stack-observer.js +3 -2
  167. package/dist/pr-drafter/index.d.ts +33 -0
  168. package/dist/pr-drafter/index.d.ts.map +1 -0
  169. package/dist/pr-drafter.d.ts +2 -35
  170. package/dist/pr-drafter.js +6 -6
  171. package/dist/prompt-firewall/index.d.ts +55 -0
  172. package/dist/prompt-firewall/index.d.ts.map +1 -0
  173. package/dist/prompt-firewall.d.ts +2 -57
  174. package/dist/prompt-firewall.js +5 -2
  175. package/dist/refactor-suggester/index.d.ts +36 -0
  176. package/dist/refactor-suggester/index.d.ts.map +1 -0
  177. package/dist/refactor-suggester.d.ts +2 -38
  178. package/dist/refactor-suggester.js +5 -5
  179. package/dist/release-notes-generator/index.d.ts +25 -0
  180. package/dist/release-notes-generator/index.d.ts.map +1 -0
  181. package/dist/release-notes-generator.d.ts +2 -27
  182. package/dist/release-notes-generator.js +28 -10
  183. package/dist/runtime/index.d.ts +124 -0
  184. package/dist/runtime/index.d.ts.map +1 -0
  185. package/dist/runtime.d.ts +1 -0
  186. package/dist/runtime.js +216 -0
  187. package/dist/schema-evolution-guard/index.d.ts +40 -0
  188. package/dist/schema-evolution-guard/index.d.ts.map +1 -0
  189. package/dist/schema-evolution-guard.d.ts +2 -42
  190. package/dist/schema-evolution-guard.js +5 -5
  191. package/dist/secret-scanner/index.d.ts +32 -0
  192. package/dist/secret-scanner/index.d.ts.map +1 -0
  193. package/dist/secret-scanner.d.ts +2 -34
  194. package/dist/secret-scanner.js +3 -2
  195. package/dist/security-hotspot-scanner/index.d.ts +28 -0
  196. package/dist/security-hotspot-scanner/index.d.ts.map +1 -0
  197. package/dist/security-hotspot-scanner.d.ts +2 -30
  198. package/dist/security-hotspot-scanner.js +5 -5
  199. package/dist/semantic-search-indexer/index.d.ts +36 -0
  200. package/dist/semantic-search-indexer/index.d.ts.map +1 -0
  201. package/dist/semantic-search-indexer.d.ts +2 -38
  202. package/dist/semantic-search-indexer.js +22 -6
  203. package/dist/semver-bump/index.d.ts +24 -0
  204. package/dist/semver-bump/index.d.ts.map +1 -0
  205. package/dist/semver-bump.d.ts +2 -26
  206. package/dist/semver-bump.js +10 -8
  207. package/dist/session-recap/index.d.ts +48 -0
  208. package/dist/session-recap/index.d.ts.map +1 -0
  209. package/dist/session-recap.d.ts +2 -50
  210. package/dist/session-recap.js +6 -4
  211. package/dist/shell-check/index.d.ts +14 -0
  212. package/dist/shell-check/index.d.ts.map +1 -0
  213. package/dist/shell-check.d.ts +2 -16
  214. package/dist/shell-check.js +6 -6
  215. package/dist/smart-rename/index.d.ts +24 -0
  216. package/dist/smart-rename/index.d.ts.map +1 -0
  217. package/dist/smart-rename.d.ts +2 -26
  218. package/dist/smart-rename.js +5 -5
  219. package/dist/spec-linker/index.d.ts +49 -0
  220. package/dist/spec-linker/index.d.ts.map +1 -0
  221. package/dist/spec-linker.d.ts +2 -51
  222. package/dist/spec-linker.js +20100 -244
  223. package/dist/template-engine/index.d.ts +13 -0
  224. package/dist/template-engine/index.d.ts.map +1 -0
  225. package/dist/template-engine.d.ts +2 -15
  226. package/dist/template-engine.js +7 -7
  227. package/dist/test-coverage-gate/index.d.ts +35 -0
  228. package/dist/test-coverage-gate/index.d.ts.map +1 -0
  229. package/dist/test-coverage-gate.d.ts +2 -37
  230. package/dist/test-coverage-gate.js +5 -5
  231. package/dist/test-flake-detector/index.d.ts +25 -0
  232. package/dist/test-flake-detector/index.d.ts.map +1 -0
  233. package/dist/test-flake-detector.d.ts +2 -27
  234. package/dist/test-flake-detector.js +98 -13
  235. package/dist/test-generator/index.d.ts +30 -0
  236. package/dist/test-generator/index.d.ts.map +1 -0
  237. package/dist/test-generator.d.ts +2 -32
  238. package/dist/test-generator.js +5 -5
  239. package/dist/test-runner-gate/index.d.ts +35 -0
  240. package/dist/test-runner-gate/index.d.ts.map +1 -0
  241. package/dist/test-runner-gate.d.ts +2 -37
  242. package/dist/test-runner-gate.js +6 -6
  243. package/dist/todo-listener/index.d.ts +35 -0
  244. package/dist/todo-listener/index.d.ts.map +1 -0
  245. package/dist/todo-listener.d.ts +2 -37
  246. package/dist/todo-listener.js +3 -2
  247. package/dist/todo-tracker/index.d.ts +4 -0
  248. package/dist/todo-tracker/index.d.ts.map +1 -0
  249. package/dist/todo-tracker.d.ts +2 -5
  250. package/dist/todo-tracker.js +5 -5
  251. package/dist/token-budget/index.d.ts +38 -0
  252. package/dist/token-budget/index.d.ts.map +1 -0
  253. package/dist/token-budget.d.ts +2 -40
  254. package/dist/token-budget.js +3 -2
  255. package/dist/token-throttle/index.d.ts +52 -0
  256. package/dist/token-throttle/index.d.ts.map +1 -0
  257. package/dist/token-throttle.d.ts +2 -54
  258. package/dist/token-throttle.js +6 -2
  259. package/dist/type-gate/index.d.ts +35 -0
  260. package/dist/type-gate/index.d.ts.map +1 -0
  261. package/dist/type-gate.d.ts +2 -37
  262. package/dist/type-gate.js +236 -43
  263. package/package.json +10 -7
@@ -0,0 +1,4 @@
1
+ import type { Plugin } from '@wrongstack/core';
2
+ declare const plugin: Plugin;
3
+ export default plugin;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,5 +1,2 @@
1
- import { Plugin } from '@wrongstack/core';
2
-
3
- declare const plugin: Plugin;
4
-
5
- export { plugin as default };
1
+ export * from './todo-tracker/index.js';
2
+ export { default } from './todo-tracker/index.js';
@@ -1,7 +1,6 @@
1
- import * as fsp from 'fs/promises';
2
- import { randomUUID } from 'crypto';
3
-
4
1
  // src/todo-tracker/index.ts
2
+ import * as fsp from "node:fs/promises";
3
+ import { randomUUID } from "node:crypto";
5
4
  function deriveFilePath(api) {
6
5
  const raw = api.config.extensions?.["todo-tracker"];
7
6
  const explicit = typeof raw?.["filePath"] === "string" ? raw["filePath"] : null;
@@ -437,5 +436,6 @@ var plugin = {
437
436
  }
438
437
  };
439
438
  var todo_tracker_default = plugin;
440
-
441
- export { todo_tracker_default as default };
439
+ export {
440
+ todo_tracker_default as default
441
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * token-budget plugin — Enforces a per-session token budget.
3
+ *
4
+ * Complements cost-tracker (which tracks but does not enforce).
5
+ * When token usage crosses the warning threshold, injects a "wrap up"
6
+ * signal into the LLM's context. When it crosses the hard limit,
7
+ * triggers a Stop hook to end the agent loop.
8
+ *
9
+ * Tools registered:
10
+ * - token_budget_status : Show budget limit, current usage, percent
11
+ * consumed, and whether the warning/stop
12
+ * threshold has been crossed.
13
+ *
14
+ * Hooks registered:
15
+ * - Stop : checks whether the budget is exhausted; if so, returns
16
+ * `decision: 'block'` with a reason so the agent loop does
17
+ * not continue.
18
+ *
19
+ * Events subscribed:
20
+ * - provider.response : accumulates prompt + completion tokens.
21
+ *
22
+ * Config surface (`config.extensions['token-budget']`):
23
+ *
24
+ * ```jsonc
25
+ * {
26
+ * "limit": 500000, // hard token limit (prompt + completion)
27
+ * "warnPercent": 80, // inject "wrap up" at this % of limit
28
+ * "stopPercent": 100, // trigger Stop at this % of limit
29
+ * "model": null // null = all models; or restrict to one
30
+ * }
31
+ * ```
32
+ *
33
+ * @public
34
+ */
35
+ import type { Plugin } from '@wrongstack/core';
36
+ declare const plugin: Plugin;
37
+ export default plugin;
38
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,40 +1,2 @@
1
- import { Plugin } from '@wrongstack/core';
2
-
3
- /**
4
- * token-budget plugin — Enforces a per-session token budget.
5
- *
6
- * Complements cost-tracker (which tracks but does not enforce).
7
- * When token usage crosses the warning threshold, injects a "wrap up"
8
- * signal into the LLM's context. When it crosses the hard limit,
9
- * triggers a Stop hook to end the agent loop.
10
- *
11
- * Tools registered:
12
- * - token_budget_status : Show budget limit, current usage, percent
13
- * consumed, and whether the warning/stop
14
- * threshold has been crossed.
15
- *
16
- * Hooks registered:
17
- * - Stop : checks whether the budget is exhausted; if so, returns
18
- * `decision: 'block'` with a reason so the agent loop does
19
- * not continue.
20
- *
21
- * Events subscribed:
22
- * - provider.response : accumulates prompt + completion tokens.
23
- *
24
- * Config surface (`config.extensions['token-budget']`):
25
- *
26
- * ```jsonc
27
- * {
28
- * "limit": 500000, // hard token limit (prompt + completion)
29
- * "warnPercent": 80, // inject "wrap up" at this % of limit
30
- * "stopPercent": 100, // trigger Stop at this % of limit
31
- * "model": null // null = all models; or restrict to one
32
- * }
33
- * ```
34
- *
35
- * @public
36
- */
37
-
38
- declare const plugin: Plugin;
39
-
40
- export { plugin as default };
1
+ export * from './token-budget/index.js';
2
+ export { default } from './token-budget/index.js';
@@ -250,5 +250,6 @@ var plugin = {
250
250
  }
251
251
  };
252
252
  var token_budget_default = plugin;
253
-
254
- export { token_budget_default as default };
253
+ export {
254
+ token_budget_default as default
255
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * token-throttle plugin — enforces a rolling-window token budget on the
3
+ * provider wire, delaying calls to stay under a rate limit.
4
+ *
5
+ * Registers an `AgentExtension.wrapProviderRunner`. Before each provider
6
+ * call it prunes a 60-second sliding window of recorded spend, estimates
7
+ * the outgoing request's tokens, and if the window's spend plus the
8
+ * estimate would exceed `tokensPerMinute`, it sleeps just long enough for
9
+ * older spend to age out of the window (capped at `maxDelayMs`). After
10
+ * the call it records the response's actual usage. This smooths bursty
11
+ * agent loops under a provider's TPM limit instead of hitting 429s.
12
+ *
13
+ * Safety posture: opt-in — loads inert until
14
+ * `config.extensions['token-throttle'].enabled = true`. Delay is bounded
15
+ * by `maxDelayMs` so a mis-set budget can never hang the agent
16
+ * indefinitely.
17
+ *
18
+ * Config (`config.extensions['token-throttle']`):
19
+ *
20
+ * ```jsonc
21
+ * {
22
+ * "enabled": false,
23
+ * "tokensPerMinute": 100000, // rolling-window budget
24
+ * "maxDelayMs": 30000, // hard cap on any single wait
25
+ * "charsPerToken": 4 // request-size → token estimate divisor
26
+ * }
27
+ * ```
28
+ *
29
+ * Tools:
30
+ * - `token_throttle_status` — window spend, throttle counters, total delay
31
+ *
32
+ * @public
33
+ */
34
+ import type { Plugin } from '@wrongstack/core';
35
+ export interface SpendEntry {
36
+ at: number;
37
+ tokens: number;
38
+ }
39
+ /** Drop entries older than the 60s window relative to `now`. */
40
+ export declare function pruneWindow(entries: SpendEntry[], now: number): SpendEntry[];
41
+ /** Sum tokens currently in the window. */
42
+ export declare function windowSpend(entries: SpendEntry[]): number;
43
+ /**
44
+ * Delay (ms) needed before a request of `projected` tokens can proceed
45
+ * without the window exceeding `limit`. 0 when it already fits. Computed
46
+ * by aging out just enough of the oldest spend: we find the earliest
47
+ * point at which enough tokens have expired to make room.
48
+ */
49
+ export declare function computeThrottleDelay(entries: SpendEntry[], now: number, limit: number, projected: number): number;
50
+ declare const plugin: Plugin;
51
+ export default plugin;
52
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,54 +1,2 @@
1
- import { Plugin } from '@wrongstack/core';
2
-
3
- /**
4
- * token-throttle plugin — enforces a rolling-window token budget on the
5
- * provider wire, delaying calls to stay under a rate limit.
6
- *
7
- * Registers an `AgentExtension.wrapProviderRunner`. Before each provider
8
- * call it prunes a 60-second sliding window of recorded spend, estimates
9
- * the outgoing request's tokens, and if the window's spend plus the
10
- * estimate would exceed `tokensPerMinute`, it sleeps just long enough for
11
- * older spend to age out of the window (capped at `maxDelayMs`). After
12
- * the call it records the response's actual usage. This smooths bursty
13
- * agent loops under a provider's TPM limit instead of hitting 429s.
14
- *
15
- * Safety posture: opt-in — loads inert until
16
- * `config.extensions['token-throttle'].enabled = true`. Delay is bounded
17
- * by `maxDelayMs` so a mis-set budget can never hang the agent
18
- * indefinitely.
19
- *
20
- * Config (`config.extensions['token-throttle']`):
21
- *
22
- * ```jsonc
23
- * {
24
- * "enabled": false,
25
- * "tokensPerMinute": 100000, // rolling-window budget
26
- * "maxDelayMs": 30000, // hard cap on any single wait
27
- * "charsPerToken": 4 // request-size → token estimate divisor
28
- * }
29
- * ```
30
- *
31
- * Tools:
32
- * - `token_throttle_status` — window spend, throttle counters, total delay
33
- *
34
- * @public
35
- */
36
-
37
- interface SpendEntry {
38
- at: number;
39
- tokens: number;
40
- }
41
- /** Drop entries older than the 60s window relative to `now`. */
42
- declare function pruneWindow(entries: SpendEntry[], now: number): SpendEntry[];
43
- /** Sum tokens currently in the window. */
44
- declare function windowSpend(entries: SpendEntry[]): number;
45
- /**
46
- * Delay (ms) needed before a request of `projected` tokens can proceed
47
- * without the window exceeding `limit`. 0 when it already fits. Computed
48
- * by aging out just enough of the oldest spend: we find the earliest
49
- * point at which enough tokens have expired to make room.
50
- */
51
- declare function computeThrottleDelay(entries: SpendEntry[], now: number, limit: number, projected: number): number;
52
- declare const plugin: Plugin;
53
-
54
- export { type SpendEntry, computeThrottleDelay, plugin as default, pruneWindow, windowSpend };
1
+ export * from './token-throttle/index.js';
2
+ export { default } from './token-throttle/index.js';
@@ -204,5 +204,9 @@ var plugin = {
204
204
  }
205
205
  };
206
206
  var token_throttle_default = plugin;
207
-
208
- export { computeThrottleDelay, token_throttle_default as default, pruneWindow, windowSpend };
207
+ export {
208
+ computeThrottleDelay,
209
+ token_throttle_default as default,
210
+ pruneWindow,
211
+ windowSpend
212
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * type-gate plugin — PostToolUse hook that runs TypeScript type-checking
3
+ * after every `write` or `edit` to a source file.
4
+ *
5
+ * `lint-gate` validates style; this plugin validates types. It runs
6
+ * `tsc --noEmit` (or a user-supplied command) and injects the first few
7
+ * errors as `additionalContext` so the model can fix type regressions in
8
+ * the same turn.
9
+ *
10
+ * Tools registered:
11
+ * - type_gate_status : Show config + per-session counters.
12
+ *
13
+ * Hooks registered:
14
+ * - PostToolUse with matcher `write|edit`.
15
+ *
16
+ * Config (`config.extensions['type-gate']`):
17
+ *
18
+ * ```jsonc
19
+ * {
20
+ * "enabled": true,
21
+ * "command": "npx tsc --noEmit", // base type-check command
22
+ * "tsConfigPath": "tsconfig.json",
23
+ * "timeoutMs": 60000,
24
+ * "failSeverity": "warn", // "warn" | "block"
25
+ * "maxErrors": 5, // errors shown in context
26
+ * "runOnChange": [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"]
27
+ * }
28
+ * ```
29
+ *
30
+ * @public
31
+ */
32
+ import type { Plugin } from '@wrongstack/core';
33
+ declare const plugin: Plugin;
34
+ export default plugin;
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/type-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AA6M/C,QAAA,MAAM,MAAM,EAAE,MAiOb,CAAC;eAEa,MAAM"}
@@ -1,37 +1,2 @@
1
- import { Plugin } from '@wrongstack/core';
2
-
3
- /**
4
- * type-gate plugin — PostToolUse hook that runs TypeScript type-checking
5
- * after every `write` or `edit` to a source file.
6
- *
7
- * `lint-gate` validates style; this plugin validates types. It runs
8
- * `tsc --noEmit` (or a user-supplied command) and injects the first few
9
- * errors as `additionalContext` so the model can fix type regressions in
10
- * the same turn.
11
- *
12
- * Tools registered:
13
- * - type_gate_status : Show config + per-session counters.
14
- *
15
- * Hooks registered:
16
- * - PostToolUse with matcher `write|edit`.
17
- *
18
- * Config (`config.extensions['type-gate']`):
19
- *
20
- * ```jsonc
21
- * {
22
- * "enabled": true,
23
- * "command": "npx tsc --noEmit", // base type-check command
24
- * "tsConfigPath": "tsconfig.json",
25
- * "timeoutMs": 60000,
26
- * "failSeverity": "warn", // "warn" | "block"
27
- * "maxErrors": 5, // errors shown in context
28
- * "runOnChange": [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts"]
29
- * }
30
- * ```
31
- *
32
- * @public
33
- */
34
-
35
- declare const plugin: Plugin;
36
-
37
- export { plugin as default };
1
+ export * from './type-gate/index.js';
2
+ export { default } from './type-gate/index.js';
package/dist/type-gate.js CHANGED
@@ -1,6 +1,195 @@
1
- import { execFileSync } from 'child_process';
2
- import { existsSync } from 'fs';
3
- import { isAbsolute, resolve, relative } from 'path';
1
+ // src/type-gate/index.ts
2
+ import { execFileSync } from "node:child_process";
3
+ import { existsSync } from "node:fs";
4
+ import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve2 } from "node:path";
5
+
6
+ // src/runtime/index.ts
7
+ import { execFile } from "node:child_process";
8
+ import { basename, isAbsolute, relative, resolve } from "node:path";
9
+ var META_CHARS = /["'`;&|<>\r\n]/;
10
+ var MAX_BUFFER_BYTES = 16 * 1024 * 1024;
11
+ function hasLeadingDash(arg) {
12
+ return arg.length > 0 && arg.startsWith("-");
13
+ }
14
+ function safeSplit(command) {
15
+ const trimmed = command.trim();
16
+ if (!trimmed || META_CHARS.test(trimmed)) return null;
17
+ return trimmed.split(/\s+/).filter(Boolean);
18
+ }
19
+ function withinProjectPath(projectRoot, candidate) {
20
+ if (candidate.length === 0 || candidate.length > 4096) return false;
21
+ if (hasLeadingDash(candidate)) return false;
22
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(projectRoot, candidate);
23
+ const rel = relative(projectRoot, resolved);
24
+ return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
25
+ }
26
+ function everyFlagAllowed(allowed, args) {
27
+ for (const arg of args) {
28
+ if (!hasLeadingDash(arg)) continue;
29
+ if (allowed === null) return false;
30
+ if (!allowed.has(arg)) return false;
31
+ }
32
+ return true;
33
+ }
34
+ function sanitizeRunnerPath(value, options = {}) {
35
+ if (!value || hasLeadingDash(value)) return null;
36
+ const projectRoot = resolve(options.projectRoot ?? process.cwd());
37
+ if (!withinProjectPath(projectRoot, value)) return null;
38
+ return isAbsolute(value) ? resolve(value) : resolve(projectRoot, value);
39
+ }
40
+ function resolveRunnerCommand(runtime, command, options = {}) {
41
+ const tokens = safeSplit(command);
42
+ if (!tokens || tokens.length === 0) return null;
43
+ const projectRoot = resolve(options.projectRoot ?? process.cwd());
44
+ const launcher = runtime.packageManager;
45
+ const [head, second, ...rest] = tokens;
46
+ if (!head) return null;
47
+ const display = tokens.join(" ");
48
+ if (launcher !== "none" && tokens.length === 1 && head === launcher) {
49
+ return null;
50
+ }
51
+ if (head === runtime.executable) {
52
+ if (!everyFlagAllowed(runtime.allowedFlags, [second, ...rest].filter((v) => Boolean(v)))) {
53
+ return null;
54
+ }
55
+ return {
56
+ cmd: head,
57
+ args: [second, ...rest].filter((v) => Boolean(v)),
58
+ display
59
+ };
60
+ }
61
+ if (launcher !== "none" && head === launcher && runtime.subcommands.length === 0 && second === runtime.executable) {
62
+ if (!everyFlagAllowed(runtime.allowedFlags, rest)) return null;
63
+ return { cmd: head, args: [second, ...rest], display };
64
+ }
65
+ if (launcher !== "none" && head === launcher && runtime.subcommands.length > 0) {
66
+ const subcommand = runtime.subcommands[0];
67
+ const exe = rest[0];
68
+ if (second === subcommand && exe === runtime.executable) {
69
+ const tail = rest.slice(1);
70
+ if (!everyFlagAllowed(runtime.allowedFlags, tail)) return null;
71
+ return { cmd: head, args: [second, exe, ...tail], display };
72
+ }
73
+ }
74
+ if (isAbsolute(head)) {
75
+ if (!withinProjectPath(projectRoot, head)) return null;
76
+ const base = basename(head);
77
+ if (base !== runtime.executable && base !== launcher) return null;
78
+ if (second !== runtime.executable) return null;
79
+ if (!everyFlagAllowed(runtime.allowedFlags, rest)) return null;
80
+ return { cmd: head, args: [second, ...rest], display };
81
+ }
82
+ return null;
83
+ }
84
+ function runRunnerCommand(argv, options) {
85
+ if (argv.length === 0) {
86
+ return Promise.resolve({
87
+ code: null,
88
+ stdout: "",
89
+ stderr: "runtime helper: empty argv",
90
+ timedOut: false,
91
+ spawnError: true
92
+ });
93
+ }
94
+ return new Promise((resolvePromise) => {
95
+ const projectRoot = resolve(options.projectRoot ?? process.cwd());
96
+ const trimmedCwd = options.cwd.trim();
97
+ if (!withinProjectPath(projectRoot, trimmedCwd)) {
98
+ resolvePromise({
99
+ code: null,
100
+ stdout: "",
101
+ stderr: "runtime helper: cwd outside project",
102
+ timedOut: false,
103
+ spawnError: true
104
+ });
105
+ return;
106
+ }
107
+ let timedOut = false;
108
+ let spawnErrored = false;
109
+ const stdoutChunks = [];
110
+ const stderrChunks = [];
111
+ let stdoutBytes = 0;
112
+ let stderrBytes = 0;
113
+ const child = execFile(
114
+ argv[0],
115
+ argv.slice(1),
116
+ {
117
+ cwd: trimmedCwd,
118
+ timeout: options.timeoutMs,
119
+ signal: options.signal,
120
+ maxBuffer: MAX_BUFFER_BYTES,
121
+ windowsHide: true,
122
+ shell: false
123
+ },
124
+ (err, stdout, stderr) => {
125
+ if (timedOut) {
126
+ resolvePromise({
127
+ code: null,
128
+ stdout: Buffer.concat([...stdoutChunks, ...Buffer.isBuffer(stdout) ? [stdout] : [Buffer.from(stdout)]]).toString("utf8"),
129
+ stderr: Buffer.concat([...stderrChunks, ...Buffer.isBuffer(stderr) ? [stderr] : [Buffer.from(stderr)]]).toString("utf8"),
130
+ timedOut: true,
131
+ spawnError: false
132
+ });
133
+ return;
134
+ }
135
+ if (spawnErrored) {
136
+ resolvePromise({
137
+ code: 127,
138
+ stdout: Buffer.concat([...stdoutChunks, ...Buffer.isBuffer(stdout) ? [stdout] : [Buffer.from(stdout)]]).toString("utf8"),
139
+ stderr: Buffer.concat([...stderrChunks, ...Buffer.isBuffer(stderr) ? [stderr] : [Buffer.from(stderr)]]).toString("utf8"),
140
+ timedOut: false,
141
+ spawnError: true
142
+ });
143
+ return;
144
+ }
145
+ if (err) {
146
+ const anyErr = err;
147
+ const code = typeof anyErr.code === "number" ? anyErr.code : 1;
148
+ resolvePromise({
149
+ code,
150
+ stdout: Buffer.concat([
151
+ ...stdoutChunks,
152
+ ...anyErr.stdout ? [Buffer.isBuffer(anyErr.stdout) ? anyErr.stdout : Buffer.from(anyErr.stdout)] : []
153
+ ]).toString("utf8"),
154
+ stderr: Buffer.concat([
155
+ ...stderrChunks,
156
+ ...anyErr.stderr ? [Buffer.isBuffer(anyErr.stderr) ? anyErr.stderr : Buffer.from(anyErr.stderr)] : []
157
+ ]).toString("utf8"),
158
+ timedOut: false,
159
+ spawnError: false
160
+ });
161
+ return;
162
+ }
163
+ resolvePromise({
164
+ code: 0,
165
+ stdout: Buffer.concat([
166
+ ...stdoutChunks,
167
+ ...Buffer.isBuffer(stdout) ? [stdout] : [Buffer.from(stdout)]
168
+ ]).toString("utf8"),
169
+ stderr: Buffer.concat([
170
+ ...stderrChunks,
171
+ ...Buffer.isBuffer(stderr) ? [stderr] : [Buffer.from(stderr)]
172
+ ]).toString("utf8"),
173
+ timedOut: false,
174
+ spawnError: false
175
+ });
176
+ }
177
+ );
178
+ child.stdout?.on("data", (chunk) => {
179
+ stdoutBytes += chunk.length;
180
+ if (stdoutBytes <= MAX_BUFFER_BYTES) stdoutChunks.push(chunk);
181
+ });
182
+ child.stderr?.on("data", (chunk) => {
183
+ stderrBytes += chunk.length;
184
+ if (stderrBytes <= MAX_BUFFER_BYTES) stderrChunks.push(chunk);
185
+ });
186
+ child.on("error", (err) => {
187
+ if (err.code === "ENOENT" || err.code === "EPERM" || err.code === "EACCES") {
188
+ spawnErrored = true;
189
+ }
190
+ });
191
+ });
192
+ }
4
193
 
5
194
  // src/type-gate/index.ts
6
195
  var API_VERSION = "^0.1.10";
@@ -39,54 +228,50 @@ function readConfig(raw) {
39
228
  function withinProject(p) {
40
229
  if (typeof p !== "string" || p.length === 0 || p.length > 4096) return false;
41
230
  const root = process.cwd();
42
- const resolved = isAbsolute(p) ? resolve(p) : resolve(root, p);
43
- const rel = relative(root, resolved);
231
+ const resolved = isAbsolute2(p) ? resolve2(p) : resolve2(root, p);
232
+ const rel = relative2(root, resolved);
44
233
  if (rel === "" || rel === ".") return true;
45
234
  if (rel.startsWith("..")) return false;
46
- if (isAbsolute(rel)) return false;
235
+ if (isAbsolute2(rel)) return false;
47
236
  return true;
48
237
  }
49
- var ALLOWED_COMMAND_TOKENS = /* @__PURE__ */ new Set([
50
- "npx",
51
- "pnpm",
52
- "npm",
53
- "yarn",
54
- "bun",
55
- "tsc",
56
- "node",
57
- "tsx"
58
- ]);
59
- function resolveAllowedCommand(customCommand) {
60
- const tokens = customCommand.split(/\s+/).filter(Boolean);
61
- if (tokens.length === 0) return null;
62
- const head = tokens[0];
63
- if (ALLOWED_COMMAND_TOKENS.has(head)) {
64
- return { cmd: head, args: tokens.slice(1) };
65
- }
66
- if (isAbsolute(head)) {
67
- if (!withinProject(head)) return null;
68
- const base = head.split(/[/\\]/).pop() ?? "";
69
- if (ALLOWED_COMMAND_TOKENS.has(base)) {
70
- return { cmd: head, args: tokens.slice(1) };
71
- }
72
- }
73
- return null;
238
+ var TSC_RUNTIME = {
239
+ id: "typescript",
240
+ packageManager: "pnpm",
241
+ executable: "tsc",
242
+ allowedFlags: /* @__PURE__ */ new Set([
243
+ "--noEmit",
244
+ "--pretty",
245
+ "--pretty=false",
246
+ "--incremental",
247
+ "--watch",
248
+ "--build",
249
+ "--filter"
250
+ ]),
251
+ subcommands: ["exec"],
252
+ defaultCommand: "pnpm exec tsc --noEmit"
253
+ };
254
+ function validateTsConfigPath(p) {
255
+ const sanitized = sanitizeRunnerPath(p);
256
+ return sanitized;
74
257
  }
75
258
  function runTypeCheck(cfg) {
76
259
  const start = Date.now();
77
- const tsConfig = cfg.tsConfigPath;
78
- let cmd;
79
- let args;
260
+ const rawTsConfig = cfg.tsConfigPath;
261
+ const validTsConfig = rawTsConfig && rawTsConfig !== "tsconfig.json" ? validateTsConfigPath(rawTsConfig) : null;
262
+ if (rawTsConfig && rawTsConfig !== "tsconfig.json" && !validTsConfig) {
263
+ return null;
264
+ }
265
+ const tsConfig = validTsConfig ?? (rawTsConfig || "tsconfig.json");
266
+ let argv;
80
267
  if (cfg.command) {
81
- const resolved = resolveAllowedCommand(cfg.command);
268
+ const resolved = resolveRunnerCommand(TSC_RUNTIME, cfg.command);
82
269
  if (!resolved) return null;
83
- cmd = resolved.cmd;
84
- args = resolved.args;
270
+ argv = [resolved.cmd, ...resolved.args];
85
271
  } else {
86
- cmd = "npx";
87
- args = ["tsc", "--noEmit"];
272
+ argv = ["npx", "tsc", "--noEmit"];
88
273
  if (tsConfig && tsConfig !== "tsconfig.json") {
89
- args.push("-p", tsConfig);
274
+ argv = [...argv, "-p", tsConfig];
90
275
  }
91
276
  }
92
277
  if (!cfg.command && tsConfig && !existsSync(tsConfig)) {
@@ -95,7 +280,7 @@ function runTypeCheck(cfg) {
95
280
  let stdout = "";
96
281
  let stderr = "";
97
282
  try {
98
- const out = execFileSync(cmd, args, {
283
+ const out = execFileSync(argv[0], argv.slice(1), {
99
284
  encoding: "utf-8",
100
285
  timeout: cfg.timeoutMs,
101
286
  cwd: process.cwd(),
@@ -109,6 +294,7 @@ function runTypeCheck(cfg) {
109
294
  stdout = e.stdout ?? "";
110
295
  stderr = e.stderr ?? "";
111
296
  }
297
+ void runRunnerCommand;
112
298
  const combined = `${stdout}
113
299
  ${stderr}`.trim();
114
300
  const durationMs = Date.now() - start;
@@ -126,6 +312,12 @@ ${stderr}`.trim();
126
312
  var plugin = {
127
313
  name: "type-gate",
128
314
  version: "0.1.0",
315
+ runtime: {
316
+ language: "typescript",
317
+ packageManager: "pnpm",
318
+ executable: "tsc",
319
+ defaultCommand: "pnpm exec tsc --noEmit"
320
+ },
129
321
  description: "PostToolUse hook that runs TypeScript type-checking after every write or edit to a source file",
130
322
  apiVersion: API_VERSION,
131
323
  capabilities: { tools: true, hooks: true },
@@ -307,5 +499,6 @@ Fix the type error(s) or adjust the change.`;
307
499
  }
308
500
  };
309
501
  var type_gate_default = plugin;
310
-
311
- export { type_gate_default as default };
502
+ export {
503
+ type_gate_default as default
504
+ };