@wrongstack/plugins 0.277.1 → 0.280.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 (72) hide show
  1. package/README.md +838 -0
  2. package/dist/auto-doc.d.ts +8 -0
  3. package/dist/auto-doc.js +175 -13
  4. package/dist/auto-escalate.d.ts +45 -0
  5. package/dist/auto-escalate.js +190 -0
  6. package/dist/branch-guard.d.ts +33 -0
  7. package/dist/branch-guard.js +228 -0
  8. package/dist/changelog-writer.d.ts +73 -0
  9. package/dist/changelog-writer.js +369 -0
  10. package/dist/checkpoint.d.ts +55 -0
  11. package/dist/checkpoint.js +305 -0
  12. package/dist/commit-validator.d.ts +33 -0
  13. package/dist/commit-validator.js +315 -0
  14. package/dist/config-validator.d.ts +48 -0
  15. package/dist/config-validator.js +347 -0
  16. package/dist/context-pins.d.ts +45 -0
  17. package/dist/context-pins.js +240 -0
  18. package/dist/cost-tracker.d.ts +40 -1
  19. package/dist/cost-tracker.js +105 -4
  20. package/dist/dep-guard.d.ts +65 -0
  21. package/dist/dep-guard.js +316 -0
  22. package/dist/diff-summary.d.ts +36 -0
  23. package/dist/diff-summary.js +235 -0
  24. package/dist/error-lens.d.ts +67 -0
  25. package/dist/error-lens.js +280 -0
  26. package/dist/format-on-save.d.ts +35 -0
  27. package/dist/format-on-save.js +219 -0
  28. package/dist/git-autocommit.js +186 -26
  29. package/dist/import-organizer.d.ts +52 -0
  30. package/dist/import-organizer.js +274 -0
  31. package/dist/index.d.ts +32 -6
  32. package/dist/index.js +10151 -1628
  33. package/dist/injection-shield.d.ts +49 -0
  34. package/dist/injection-shield.js +205 -0
  35. package/dist/lint-gate.d.ts +33 -0
  36. package/dist/lint-gate.js +394 -0
  37. package/dist/llm-cache.d.ts +56 -0
  38. package/dist/llm-cache.js +251 -0
  39. package/dist/loop-breaker.d.ts +43 -0
  40. package/dist/loop-breaker.js +241 -0
  41. package/dist/model-router.d.ts +69 -0
  42. package/dist/model-router.js +198 -0
  43. package/dist/notify-hub.d.ts +45 -0
  44. package/dist/notify-hub.js +304 -0
  45. package/dist/path-guard.d.ts +54 -0
  46. package/dist/path-guard.js +235 -0
  47. package/dist/prompt-firewall.d.ts +57 -0
  48. package/dist/prompt-firewall.js +290 -0
  49. package/dist/secret-scanner.d.ts +34 -0
  50. package/dist/secret-scanner.js +409 -0
  51. package/dist/semver-bump.js +45 -0
  52. package/dist/session-recap.d.ts +50 -0
  53. package/dist/session-recap.js +421 -0
  54. package/dist/shell-check.js +52 -4
  55. package/dist/spec-linker.d.ts +51 -0
  56. package/dist/spec-linker.js +541 -0
  57. package/dist/template-engine.js +19 -1
  58. package/dist/test-runner-gate.d.ts +37 -0
  59. package/dist/test-runner-gate.js +356 -0
  60. package/dist/todo-listener.d.ts +37 -0
  61. package/dist/todo-listener.js +216 -0
  62. package/dist/todo-tracker.d.ts +5 -0
  63. package/dist/todo-tracker.js +441 -0
  64. package/dist/token-budget.d.ts +40 -0
  65. package/dist/token-budget.js +254 -0
  66. package/dist/token-throttle.d.ts +54 -0
  67. package/dist/token-throttle.js +203 -0
  68. package/package.json +116 -12
  69. package/dist/json-path.d.ts +0 -18
  70. package/dist/json-path.js +0 -15
  71. package/dist/web-search.d.ts +0 -19
  72. package/dist/web-search.js +0 -15
@@ -0,0 +1,254 @@
1
+ // src/token-budget/index.ts
2
+ var API_VERSION = "^0.1.10";
3
+ var state = {
4
+ totalTokens: 0,
5
+ totalPromptTokens: 0,
6
+ totalCompletionTokens: 0,
7
+ requestCount: 0,
8
+ /** Whether the warning has already fired (one-shot). */
9
+ warningFired: false,
10
+ /** Whether the stop has already fired (one-shot). */
11
+ stopFired: false,
12
+ /** Whether the warning context has been injected into the LLM (one-shot). */
13
+ warnContextInjected: false,
14
+ /** Whether the stop context has been injected into the LLM (one-shot). */
15
+ stopContextInjected: false,
16
+ /** Stop hook unregister handle. */
17
+ hookUnregister: null,
18
+ /** PostToolUse hook unregister handle. */
19
+ postHookUnregister: null,
20
+ /** Last token breakdown — surfaced by health(). */
21
+ lastRequest: null
22
+ };
23
+ var DEFAULTS = {
24
+ limit: 0,
25
+ warnPercent: 80,
26
+ stopPercent: 100,
27
+ model: ""
28
+ };
29
+ function modelMatches(pattern, model) {
30
+ if (pattern === "") return true;
31
+ if (pattern.endsWith("*")) {
32
+ const prefix = pattern.slice(0, -1).toLowerCase();
33
+ return model.toLowerCase().startsWith(prefix);
34
+ }
35
+ return model.toLowerCase() === pattern.toLowerCase();
36
+ }
37
+ function readConfig(raw) {
38
+ if (!raw || typeof raw !== "object") return { ...DEFAULTS };
39
+ const r = raw;
40
+ return {
41
+ limit: typeof r["limit"] === "number" ? r["limit"] : DEFAULTS.limit,
42
+ warnPercent: typeof r["warnPercent"] === "number" ? r["warnPercent"] : DEFAULTS.warnPercent,
43
+ stopPercent: typeof r["stopPercent"] === "number" ? r["stopPercent"] : DEFAULTS.stopPercent,
44
+ model: typeof r["model"] === "string" ? r["model"] : ""
45
+ };
46
+ }
47
+ var plugin = {
48
+ name: "token-budget",
49
+ version: "0.1.0",
50
+ description: "Enforces a per-session token budget \u2014 warns at a threshold and stops the agent loop when the limit is hit",
51
+ apiVersion: API_VERSION,
52
+ capabilities: { tools: true, hooks: true },
53
+ defaultConfig: { ...DEFAULTS },
54
+ configSchema: {
55
+ type: "object",
56
+ properties: {
57
+ limit: {
58
+ type: "number",
59
+ minimum: 0,
60
+ default: 0,
61
+ description: "Hard token limit (prompt + completion combined). 0 = disabled (tracking only)."
62
+ },
63
+ warnPercent: {
64
+ type: "number",
65
+ minimum: 1,
66
+ maximum: 100,
67
+ default: 80,
68
+ description: 'Percentage of limit at which to inject a "wrap up" context signal.'
69
+ },
70
+ stopPercent: {
71
+ type: "number",
72
+ minimum: 1,
73
+ maximum: 100,
74
+ default: 100,
75
+ description: "Percentage of limit at which to trigger Stop (end the agent loop)."
76
+ },
77
+ model: {
78
+ type: "string",
79
+ default: "",
80
+ description: 'Restrict counting to a specific model. Supports "*" trailing wildcard (e.g. "gpt-4*" matches gpt-4o, gpt-4o-mini). Empty string = count all models.'
81
+ }
82
+ }
83
+ },
84
+ setup(api) {
85
+ state.totalTokens = 0;
86
+ state.totalPromptTokens = 0;
87
+ state.totalCompletionTokens = 0;
88
+ state.requestCount = 0;
89
+ state.warningFired = false;
90
+ state.stopFired = false;
91
+ state.warnContextInjected = false;
92
+ state.stopContextInjected = false;
93
+ state.hookUnregister = null;
94
+ state.postHookUnregister = null;
95
+ state.lastRequest = null;
96
+ const cfg = readConfig(api.config.extensions?.["token-budget"]);
97
+ api.onEvent("provider.response", (payload) => {
98
+ const p = payload;
99
+ const usage = p?.usage;
100
+ if (!usage) return;
101
+ if (cfg.model !== "" && p?.ctx?.model !== void 0) {
102
+ const model = p.ctx.model;
103
+ if (!modelMatches(cfg.model, model)) return;
104
+ }
105
+ const promptTokens = usage.input ?? 0;
106
+ const completionTokens = usage.output ?? 0;
107
+ const total = promptTokens + completionTokens;
108
+ state.totalPromptTokens += promptTokens;
109
+ state.totalCompletionTokens += completionTokens;
110
+ state.totalTokens += total;
111
+ state.requestCount += 1;
112
+ state.lastRequest = {
113
+ model: p?.ctx?.model ?? "unknown",
114
+ prompt: promptTokens,
115
+ completion: completionTokens,
116
+ when: (/* @__PURE__ */ new Date()).toISOString()
117
+ };
118
+ if (cfg.limit <= 0) return;
119
+ const percent = state.totalTokens / cfg.limit * 100;
120
+ if (!state.warningFired && percent >= cfg.warnPercent && percent < cfg.stopPercent) {
121
+ state.warningFired = true;
122
+ const remaining = cfg.limit - state.totalTokens;
123
+ api.log.info("token-budget: warning threshold reached", {
124
+ percent: Math.round(percent),
125
+ remaining
126
+ });
127
+ api.emitCustom("token-budget:warning", {
128
+ percent: Math.round(percent),
129
+ remaining,
130
+ total: state.totalTokens,
131
+ limit: cfg.limit
132
+ });
133
+ }
134
+ if (!state.stopFired && percent >= cfg.stopPercent) {
135
+ state.stopFired = true;
136
+ api.log.warn("token-budget: hard limit reached \u2014 agent loop will be stopped", {
137
+ total: state.totalTokens,
138
+ limit: cfg.limit
139
+ });
140
+ api.emitCustom("token-budget:limit_reached", {
141
+ total: state.totalTokens,
142
+ limit: cfg.limit
143
+ });
144
+ }
145
+ });
146
+ state.hookUnregister = api.registerHook("Stop", void 0, () => {
147
+ if (cfg.limit <= 0 || !state.stopFired) return;
148
+ return {
149
+ decision: "block",
150
+ reason: `token-budget: session token limit reached (${state.totalTokens.toLocaleString()} / ${cfg.limit.toLocaleString()} tokens). The budget is exhausted \u2014 wrap up the current task and summarize what was accomplished.`
151
+ };
152
+ });
153
+ state.postHookUnregister = api.registerHook("PostToolUse", "*", () => {
154
+ if (cfg.limit <= 0) return;
155
+ const percent = Math.round(state.totalTokens / cfg.limit * 100);
156
+ const remaining = Math.max(cfg.limit - state.totalTokens, 0);
157
+ if (state.stopFired && !state.stopContextInjected) {
158
+ state.stopContextInjected = true;
159
+ return {
160
+ additionalContext: `
161
+ \u{1F6D1} token-budget: HARD LIMIT REACHED \u2014 ${state.totalTokens.toLocaleString()} / ${cfg.limit.toLocaleString()} tokens (${percent}%). You must stop here. Do NOT start any new task. Summarize what was accomplished and list any remaining work.`
162
+ };
163
+ }
164
+ if (state.warningFired && !state.warnContextInjected) {
165
+ state.warnContextInjected = true;
166
+ return {
167
+ additionalContext: `
168
+ \u26A0\uFE0F token-budget: ${percent}% of budget used (${state.totalTokens.toLocaleString()} / ${cfg.limit.toLocaleString()} tokens, ${remaining.toLocaleString()} remaining). Start wrapping up \u2014 prioritize finishing the current task over starting new ones.`
169
+ };
170
+ }
171
+ return;
172
+ });
173
+ api.tools.register({
174
+ name: "token_budget_status",
175
+ description: "Shows the current token budget status: limit, consumed, remaining, percentage, and whether warning/stop thresholds have been crossed.",
176
+ inputSchema: { type: "object", properties: {} },
177
+ permission: "auto",
178
+ category: "Meta",
179
+ mutating: false,
180
+ async execute() {
181
+ const consumed = state.totalTokens;
182
+ const limit = cfg.limit;
183
+ const percent = limit > 0 ? Math.round(consumed / limit * 100) : 0;
184
+ const remaining = limit > 0 ? Math.max(limit - consumed, 0) : Infinity;
185
+ return {
186
+ ok: true,
187
+ limit,
188
+ consumed,
189
+ remaining,
190
+ percent,
191
+ requestCount: state.requestCount,
192
+ breakdown: {
193
+ prompt: state.totalPromptTokens,
194
+ completion: state.totalCompletionTokens
195
+ },
196
+ warningFired: state.warningFired,
197
+ stopFired: state.stopFired,
198
+ lastRequest: state.lastRequest
199
+ };
200
+ }
201
+ });
202
+ api.log.info("token-budget plugin loaded", {
203
+ version: "0.1.0",
204
+ limit: cfg.limit > 0 ? cfg.limit.toLocaleString() : "unlimited",
205
+ warnPercent: cfg.warnPercent,
206
+ stopPercent: cfg.stopPercent
207
+ });
208
+ },
209
+ teardown(api) {
210
+ if (state.hookUnregister) {
211
+ try {
212
+ state.hookUnregister();
213
+ } catch {
214
+ }
215
+ state.hookUnregister = null;
216
+ }
217
+ if (state.postHookUnregister) {
218
+ try {
219
+ state.postHookUnregister();
220
+ } catch {
221
+ }
222
+ state.postHookUnregister = null;
223
+ }
224
+ const final = {
225
+ totalTokens: state.totalTokens,
226
+ requestCount: state.requestCount,
227
+ warningFired: state.warningFired,
228
+ stopFired: state.stopFired
229
+ };
230
+ state.totalTokens = 0;
231
+ state.totalPromptTokens = 0;
232
+ state.totalCompletionTokens = 0;
233
+ state.requestCount = 0;
234
+ state.warningFired = false;
235
+ state.stopFired = false;
236
+ state.warnContextInjected = false;
237
+ state.stopContextInjected = false;
238
+ state.lastRequest = null;
239
+ api.log.info("token-budget: teardown complete", { final });
240
+ },
241
+ async health() {
242
+ return {
243
+ ok: true,
244
+ message: state.lastRequest === null ? `token-budget: ${state.totalTokens.toLocaleString()} tokens across ${state.requestCount} request(s)` : `token-budget: ${state.totalTokens.toLocaleString()} tokens, last ${state.lastRequest.model} at ${state.lastRequest.when}`,
245
+ totalTokens: state.totalTokens,
246
+ requestCount: state.requestCount,
247
+ warningFired: state.warningFired,
248
+ stopFired: state.stopFired
249
+ };
250
+ }
251
+ };
252
+ var token_budget_default = plugin;
253
+
254
+ export { token_budget_default as default };
@@ -0,0 +1,54 @@
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 };
@@ -0,0 +1,203 @@
1
+ // src/token-throttle/index.ts
2
+ var WINDOW_MS = 6e4;
3
+ var DEFAULTS = {
4
+ enabled: false,
5
+ tokensPerMinute: 1e5,
6
+ maxDelayMs: 3e4,
7
+ charsPerToken: 4
8
+ };
9
+ function readConfig(raw) {
10
+ if (!raw || typeof raw !== "object") return { ...DEFAULTS };
11
+ const r = raw;
12
+ return {
13
+ enabled: r["enabled"] === true,
14
+ tokensPerMinute: typeof r["tokensPerMinute"] === "number" && r["tokensPerMinute"] > 0 ? r["tokensPerMinute"] : DEFAULTS.tokensPerMinute,
15
+ maxDelayMs: typeof r["maxDelayMs"] === "number" && r["maxDelayMs"] >= 0 ? r["maxDelayMs"] : DEFAULTS.maxDelayMs,
16
+ charsPerToken: typeof r["charsPerToken"] === "number" && r["charsPerToken"] >= 1 ? r["charsPerToken"] : DEFAULTS.charsPerToken
17
+ };
18
+ }
19
+ function pruneWindow(entries, now) {
20
+ return entries.filter((e) => now - e.at < WINDOW_MS);
21
+ }
22
+ function windowSpend(entries) {
23
+ return entries.reduce((sum, e) => sum + e.tokens, 0);
24
+ }
25
+ function computeThrottleDelay(entries, now, limit, projected) {
26
+ const current = windowSpend(entries);
27
+ if (current + projected <= limit) return 0;
28
+ const mustFree = current + projected - limit;
29
+ const sorted = [...entries].sort((a, b) => a.at - b.at);
30
+ let freed = 0;
31
+ for (const e of sorted) {
32
+ freed += e.tokens;
33
+ if (freed >= mustFree) {
34
+ const leavesAt = e.at + WINDOW_MS;
35
+ return Math.max(0, leavesAt - now);
36
+ }
37
+ }
38
+ const oldest = sorted[0];
39
+ return oldest ? Math.max(0, oldest.at + WINDOW_MS - now) : 0;
40
+ }
41
+ var state = {
42
+ window: [],
43
+ invocations: 0,
44
+ throttled: 0,
45
+ totalDelayMs: 0,
46
+ extensionUnregister: null
47
+ };
48
+ function estimateRequestTokens(request, charsPerToken) {
49
+ let chars = 0;
50
+ const walk = (v) => {
51
+ if (typeof v === "string") chars += v.length;
52
+ else if (Array.isArray(v)) for (const i of v) walk(i);
53
+ else if (v && typeof v === "object") {
54
+ const o = v;
55
+ if (typeof o["text"] === "string") chars += o["text"].length;
56
+ if (o["content"] !== void 0) walk(o["content"]);
57
+ }
58
+ };
59
+ walk(request["system"]);
60
+ walk(request["messages"]);
61
+ return Math.ceil(chars / charsPerToken);
62
+ }
63
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
64
+ var plugin = {
65
+ name: "token-throttle",
66
+ version: "0.1.0",
67
+ description: "Rolling-window tokens/min budget that delays provider calls to stay under a rate limit (wrapProviderRunner). Opt-in; delay capped by maxDelayMs.",
68
+ apiVersion: "^0.1.10",
69
+ capabilities: { tools: true },
70
+ defaultConfig: { ...DEFAULTS },
71
+ configSchema: {
72
+ type: "object",
73
+ properties: {
74
+ enabled: {
75
+ type: "boolean",
76
+ default: false,
77
+ description: "Master switch. OFF by default because throttling injects delays into provider calls."
78
+ },
79
+ tokensPerMinute: {
80
+ type: "number",
81
+ minimum: 1,
82
+ default: 1e5,
83
+ description: "Rolling 60s-window token budget."
84
+ },
85
+ maxDelayMs: {
86
+ type: "number",
87
+ minimum: 0,
88
+ default: 3e4,
89
+ description: "Hard cap on any single throttle wait so a mis-set budget cannot hang the agent."
90
+ },
91
+ charsPerToken: {
92
+ type: "number",
93
+ minimum: 1,
94
+ default: 4,
95
+ description: "Divisor turning request char size into a token estimate."
96
+ }
97
+ }
98
+ },
99
+ setup(api) {
100
+ state.window = [];
101
+ state.invocations = 0;
102
+ state.throttled = 0;
103
+ state.totalDelayMs = 0;
104
+ if (state.extensionUnregister) {
105
+ try {
106
+ state.extensionUnregister();
107
+ } catch {
108
+ }
109
+ state.extensionUnregister = null;
110
+ }
111
+ const cfg = readConfig(api.config.extensions?.["token-throttle"]);
112
+ if (cfg.enabled) {
113
+ state.extensionUnregister = api.extensions.register({
114
+ name: "token-throttle",
115
+ owner: "token-throttle",
116
+ async wrapProviderRunner(_ctx, request, inner) {
117
+ const req = request ?? {};
118
+ state.invocations += 1;
119
+ const now = Date.now();
120
+ state.window = pruneWindow(state.window, now);
121
+ const projected = estimateRequestTokens(req, cfg.charsPerToken);
122
+ const rawDelay = computeThrottleDelay(state.window, now, cfg.tokensPerMinute, projected);
123
+ const delay = Math.min(rawDelay, cfg.maxDelayMs);
124
+ if (delay > 0) {
125
+ state.throttled += 1;
126
+ state.totalDelayMs += delay;
127
+ api.metrics.counter("throttled");
128
+ api.metrics.histogram("delay_ms", delay);
129
+ api.log.info("token-throttle: delaying provider call", { delayMs: delay, projected });
130
+ await sleep(delay);
131
+ }
132
+ const response = await inner(_ctx, request);
133
+ const used = (response?.usage?.input ?? 0) + (response?.usage?.output ?? 0) || projected;
134
+ state.window.push({ at: Date.now(), tokens: used });
135
+ return response;
136
+ }
137
+ });
138
+ }
139
+ api.tools.register({
140
+ name: "token_throttle_status",
141
+ description: "Reports token-throttle state: window spend, throttle counters, and total injected delay.",
142
+ inputSchema: { type: "object", properties: {} },
143
+ permission: "auto",
144
+ category: "Diagnostics",
145
+ mutating: false,
146
+ async execute() {
147
+ const now = Date.now();
148
+ const live = pruneWindow(state.window, now);
149
+ return {
150
+ ok: true,
151
+ enabled: cfg.enabled,
152
+ tokensPerMinute: cfg.tokensPerMinute,
153
+ maxDelayMs: cfg.maxDelayMs,
154
+ windowSpend: windowSpend(live),
155
+ windowEntries: live.length,
156
+ counters: {
157
+ invocations: state.invocations,
158
+ throttled: state.throttled,
159
+ totalDelayMs: state.totalDelayMs
160
+ }
161
+ };
162
+ }
163
+ });
164
+ api.log.info("token-throttle plugin loaded", {
165
+ version: "0.1.0",
166
+ enabled: cfg.enabled,
167
+ tokensPerMinute: cfg.tokensPerMinute
168
+ });
169
+ },
170
+ teardown(api) {
171
+ if (state.extensionUnregister) {
172
+ try {
173
+ state.extensionUnregister();
174
+ } catch {
175
+ }
176
+ state.extensionUnregister = null;
177
+ }
178
+ const final = {
179
+ invocations: state.invocations,
180
+ throttled: state.throttled,
181
+ totalDelayMs: state.totalDelayMs
182
+ };
183
+ state.window = [];
184
+ state.invocations = 0;
185
+ state.throttled = 0;
186
+ state.totalDelayMs = 0;
187
+ api.log.info("token-throttle: teardown complete", { final });
188
+ },
189
+ async health() {
190
+ return {
191
+ ok: true,
192
+ message: `token-throttle: ${state.throttled} throttle(s) of ${state.invocations} call(s), ${state.totalDelayMs}ms total delay`,
193
+ counters: {
194
+ invocations: state.invocations,
195
+ throttled: state.throttled,
196
+ totalDelayMs: state.totalDelayMs
197
+ }
198
+ };
199
+ }
200
+ };
201
+ var token_throttle_default = plugin;
202
+
203
+ export { computeThrottleDelay, token_throttle_default as default, pruneWindow, windowSpend };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wrongstack/plugins",
3
- "version": "0.277.1",
4
- "description": "Official WrongStack plugin collection — auto-doc, git-autocommit, shell-check, cost-tracker, file-watcher, web-search, json-path, cron, template-engine, semver-bump",
3
+ "version": "0.280.0",
4
+ "description": "Official WrongStack plugin collection — auto-doc, git-autocommit, shell-check, cost-tracker, file-watcher, cron, template-engine, semver-bump, secret-scanner, todo-tracker, token-budget, lint-gate, branch-guard, diff-summary, commit-validator, format-on-save, test-runner-gate, import-organizer, todo-listener, session-recap, spec-linker, loop-breaker, path-guard, context-pins, checkpoint, error-lens, dep-guard, config-validator, notify-hub, changelog-writer, injection-shield, llm-cache, model-router, prompt-firewall, auto-escalate, token-throttle",
5
5
  "license": "MIT",
6
6
  "author": "ECOSTACK TECHNOLOGY OÜ",
7
7
  "type": "module",
@@ -32,14 +32,6 @@
32
32
  "types": "./dist/file-watcher.d.ts",
33
33
  "import": "./dist/file-watcher.js"
34
34
  },
35
- "./web-search": {
36
- "types": "./dist/web-search.d.ts",
37
- "import": "./dist/web-search.js"
38
- },
39
- "./json-path": {
40
- "types": "./dist/json-path.d.ts",
41
- "import": "./dist/json-path.js"
42
- },
43
35
  "./cron": {
44
36
  "types": "./dist/cron.d.ts",
45
37
  "import": "./dist/cron.js"
@@ -51,6 +43,118 @@
51
43
  "./semver-bump": {
52
44
  "types": "./dist/semver-bump.d.ts",
53
45
  "import": "./dist/semver-bump.js"
46
+ },
47
+ "./secret-scanner": {
48
+ "types": "./dist/secret-scanner.d.ts",
49
+ "import": "./dist/secret-scanner.js"
50
+ },
51
+ "./todo-tracker": {
52
+ "types": "./dist/todo-tracker.d.ts",
53
+ "import": "./dist/todo-tracker.js"
54
+ },
55
+ "./token-budget": {
56
+ "types": "./dist/token-budget.d.ts",
57
+ "import": "./dist/token-budget.js"
58
+ },
59
+ "./lint-gate": {
60
+ "types": "./dist/lint-gate.d.ts",
61
+ "import": "./dist/lint-gate.js"
62
+ },
63
+ "./branch-guard": {
64
+ "types": "./dist/branch-guard.d.ts",
65
+ "import": "./dist/branch-guard.js"
66
+ },
67
+ "./diff-summary": {
68
+ "types": "./dist/diff-summary.d.ts",
69
+ "import": "./dist/diff-summary.js"
70
+ },
71
+ "./commit-validator": {
72
+ "types": "./dist/commit-validator.d.ts",
73
+ "import": "./dist/commit-validator.js"
74
+ },
75
+ "./format-on-save": {
76
+ "types": "./dist/format-on-save.d.ts",
77
+ "import": "./dist/format-on-save.js"
78
+ },
79
+ "./test-runner-gate": {
80
+ "types": "./dist/test-runner-gate.d.ts",
81
+ "import": "./dist/test-runner-gate.js"
82
+ },
83
+ "./import-organizer": {
84
+ "types": "./dist/import-organizer.d.ts",
85
+ "import": "./dist/import-organizer.js"
86
+ },
87
+ "./todo-listener": {
88
+ "types": "./dist/todo-listener.d.ts",
89
+ "import": "./dist/todo-listener.js"
90
+ },
91
+ "./session-recap": {
92
+ "types": "./dist/session-recap.d.ts",
93
+ "import": "./dist/session-recap.js"
94
+ },
95
+ "./spec-linker": {
96
+ "types": "./dist/spec-linker.d.ts",
97
+ "import": "./dist/spec-linker.js"
98
+ },
99
+ "./loop-breaker": {
100
+ "types": "./dist/loop-breaker.d.ts",
101
+ "import": "./dist/loop-breaker.js"
102
+ },
103
+ "./path-guard": {
104
+ "types": "./dist/path-guard.d.ts",
105
+ "import": "./dist/path-guard.js"
106
+ },
107
+ "./context-pins": {
108
+ "types": "./dist/context-pins.d.ts",
109
+ "import": "./dist/context-pins.js"
110
+ },
111
+ "./checkpoint": {
112
+ "types": "./dist/checkpoint.d.ts",
113
+ "import": "./dist/checkpoint.js"
114
+ },
115
+ "./error-lens": {
116
+ "types": "./dist/error-lens.d.ts",
117
+ "import": "./dist/error-lens.js"
118
+ },
119
+ "./dep-guard": {
120
+ "types": "./dist/dep-guard.d.ts",
121
+ "import": "./dist/dep-guard.js"
122
+ },
123
+ "./config-validator": {
124
+ "types": "./dist/config-validator.d.ts",
125
+ "import": "./dist/config-validator.js"
126
+ },
127
+ "./notify-hub": {
128
+ "types": "./dist/notify-hub.d.ts",
129
+ "import": "./dist/notify-hub.js"
130
+ },
131
+ "./changelog-writer": {
132
+ "types": "./dist/changelog-writer.d.ts",
133
+ "import": "./dist/changelog-writer.js"
134
+ },
135
+ "./injection-shield": {
136
+ "types": "./dist/injection-shield.d.ts",
137
+ "import": "./dist/injection-shield.js"
138
+ },
139
+ "./llm-cache": {
140
+ "types": "./dist/llm-cache.d.ts",
141
+ "import": "./dist/llm-cache.js"
142
+ },
143
+ "./model-router": {
144
+ "types": "./dist/model-router.d.ts",
145
+ "import": "./dist/model-router.js"
146
+ },
147
+ "./prompt-firewall": {
148
+ "types": "./dist/prompt-firewall.d.ts",
149
+ "import": "./dist/prompt-firewall.js"
150
+ },
151
+ "./auto-escalate": {
152
+ "types": "./dist/auto-escalate.d.ts",
153
+ "import": "./dist/auto-escalate.js"
154
+ },
155
+ "./token-throttle": {
156
+ "types": "./dist/token-throttle.d.ts",
157
+ "import": "./dist/token-throttle.js"
54
158
  }
55
159
  },
56
160
  "files": [
@@ -63,8 +167,8 @@
63
167
  "vitest": "^4.1.9"
64
168
  },
65
169
  "dependencies": {
66
- "@wrongstack/tools": "0.277.1",
67
- "@wrongstack/core": "0.277.1"
170
+ "@wrongstack/core": "0.280.0",
171
+ "@wrongstack/tools": "0.280.0"
68
172
  },
69
173
  "scripts": {
70
174
  "build": "tsup",
@@ -1,18 +0,0 @@
1
- import { Plugin } from '@wrongstack/core';
2
-
3
- /**
4
- * json-path plugin — RETIRED.
5
- *
6
- * The four tools this plugin previously registered (`jmespath_query`,
7
- * `json_validate`, `json_transform`, `json_merge`) have been consolidated
8
- * into the built-in `json` tool (packages/tools/src/json.ts) via its
9
- * `action` parameter.
10
- *
11
- * This file remains as a no-op stub so existing config references
12
- * (`"json-path": { "enabled": true }`) do not break. It registers no
13
- * tools and logs a deprecation notice on load.
14
- */
15
-
16
- declare const plugin: Plugin;
17
-
18
- export { plugin as default };