@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,274 @@
1
+ import { spawn } from 'child_process';
2
+ import { existsSync, statSync } from 'fs';
3
+
4
+ // src/import-organizer/index.ts
5
+ var API_VERSION = "^0.1.10";
6
+ var state = {
7
+ invocationCount: 0,
8
+ organizedCount: 0,
9
+ cleanCount: 0,
10
+ errorCount: 0,
11
+ hookUnregister: null,
12
+ lastResult: null,
13
+ probeComplete: false,
14
+ linterAvailable: false
15
+ };
16
+ var DEFAULTS = {
17
+ enabled: true,
18
+ command: "npx @biomejs/biome check --write --unsafe",
19
+ fallbackCommand: "npx eslint --fix",
20
+ timeoutMs: 1e4
21
+ };
22
+ function readConfig(raw) {
23
+ if (!raw || typeof raw !== "object") return { ...DEFAULTS };
24
+ const r = raw;
25
+ return {
26
+ enabled: r["enabled"] !== false,
27
+ command: typeof r["command"] === "string" && r["command"].length > 0 ? r["command"] : DEFAULTS.command,
28
+ fallbackCommand: typeof r["fallbackCommand"] === "string" && r["fallbackCommand"].length > 0 ? r["fallbackCommand"] : DEFAULTS.fallbackCommand,
29
+ timeoutMs: typeof r["timeoutMs"] === "number" && r["timeoutMs"] > 0 ? r["timeoutMs"] : DEFAULTS.timeoutMs
30
+ };
31
+ }
32
+ function runCommand(command, args, timeoutMs, cwd) {
33
+ return new Promise((resolve) => {
34
+ let timedOut = false;
35
+ const stdoutChunks = [];
36
+ const stderrChunks = [];
37
+ let child;
38
+ try {
39
+ child = spawn(command, args, {
40
+ cwd,
41
+ stdio: ["ignore", "pipe", "pipe"],
42
+ signal: AbortSignal.timeout(timeoutMs)
43
+ });
44
+ } catch {
45
+ resolve({ code: 127, stdout: "", stderr: "", timedOut: false });
46
+ return;
47
+ }
48
+ child.stdout?.on("data", (c) => stdoutChunks.push(c));
49
+ child.stderr?.on("data", (c) => stderrChunks.push(c));
50
+ child.on("error", () => {
51
+ resolve({ code: 127, stdout: "", stderr: "", timedOut: false });
52
+ });
53
+ child.on("close", (code) => {
54
+ if (timedOut) return;
55
+ resolve({
56
+ code,
57
+ stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
58
+ stderr: Buffer.concat(stderrChunks).toString("utf-8"),
59
+ timedOut: false
60
+ });
61
+ });
62
+ child.on("abort", () => {
63
+ timedOut = true;
64
+ resolve({ code: null, stdout: "", stderr: "", timedOut: true });
65
+ });
66
+ });
67
+ }
68
+ async function organizeImports(filePath, cfg, cwd) {
69
+ if (!existsSync(filePath)) return null;
70
+ let bytesBefore;
71
+ try {
72
+ bytesBefore = statSync(filePath).size;
73
+ } catch {
74
+ return null;
75
+ }
76
+ const primary = cfg.command.split(/\s+/).filter(Boolean);
77
+ if (primary.length === 0) return null;
78
+ const [primaryCmd, ...primaryArgs] = primary;
79
+ let result = await runCommand(primaryCmd, [...primaryArgs, filePath], cfg.timeoutMs, cwd);
80
+ let usedCommand = cfg.command;
81
+ if (result.code === 127 && cfg.fallbackCommand) {
82
+ const fallback = cfg.fallbackCommand.split(/\s+/).filter(Boolean);
83
+ if (fallback.length > 0) {
84
+ const [fbCmd, ...fbArgs] = fallback;
85
+ result = await runCommand(fbCmd, [...fbArgs, filePath], cfg.timeoutMs, cwd);
86
+ usedCommand = cfg.fallbackCommand;
87
+ }
88
+ }
89
+ if (result.timedOut || result.code === null) return null;
90
+ if (result.code === 127) return null;
91
+ let bytesAfter;
92
+ try {
93
+ bytesAfter = statSync(filePath).size;
94
+ } catch {
95
+ return null;
96
+ }
97
+ return {
98
+ changed: bytesAfter !== bytesBefore,
99
+ bytesBefore,
100
+ bytesAfter,
101
+ command: usedCommand,
102
+ stderr: result.stderr
103
+ };
104
+ }
105
+ var plugin = {
106
+ name: "import-organizer",
107
+ version: "0.1.0",
108
+ description: "PostToolUse hook that re-sorts and de-duplicates imports in a file after every write or edit",
109
+ apiVersion: API_VERSION,
110
+ capabilities: { tools: true, hooks: true },
111
+ defaultConfig: { ...DEFAULTS },
112
+ configSchema: {
113
+ type: "object",
114
+ properties: {
115
+ enabled: {
116
+ type: "boolean",
117
+ default: true,
118
+ description: "Master switch. When false, the hook is a no-op."
119
+ },
120
+ command: {
121
+ type: "string",
122
+ default: DEFAULTS.command,
123
+ description: "Primary linter command. Use the `--write` (or `--fix`) flag and biome-specific `--unsafe` so import organization runs."
124
+ },
125
+ fallbackCommand: {
126
+ type: "string",
127
+ default: DEFAULTS.fallbackCommand,
128
+ description: "Fallback command (e.g. `eslint --fix`) used when the primary linter is not installed."
129
+ },
130
+ timeoutMs: {
131
+ type: "number",
132
+ minimum: 1e3,
133
+ default: 1e4,
134
+ description: "Per-invocation linter timeout in milliseconds."
135
+ }
136
+ }
137
+ },
138
+ setup(api) {
139
+ state.invocationCount = 0;
140
+ state.organizedCount = 0;
141
+ state.cleanCount = 0;
142
+ state.errorCount = 0;
143
+ state.hookUnregister = null;
144
+ state.lastResult = null;
145
+ state.probeComplete = false;
146
+ state.linterAvailable = false;
147
+ const cfg = readConfig(api.config.extensions?.["import-organizer"]);
148
+ const hook = async (input) => {
149
+ if (!cfg.enabled) return;
150
+ if (input.toolResult?.isError) return;
151
+ const toolName = input.toolName ?? "";
152
+ const inp = input.toolInput ?? {};
153
+ const filePath = inp["path"];
154
+ if (!filePath || typeof filePath !== "string") return;
155
+ const ext = filePath.includes(".") ? filePath.slice(filePath.lastIndexOf(".")) : "";
156
+ if (![".ts", ".tsx", ".js", ".jsx", ".mjs", ".mts"].includes(ext)) return;
157
+ state.invocationCount += 1;
158
+ const result = await organizeImports(filePath, cfg, process.cwd());
159
+ if (!result) {
160
+ if (!state.linterAvailable) {
161
+ state.linterAvailable = false;
162
+ state.probeComplete = true;
163
+ api.log.warn("import-organizer: no linter available \u2014 hook will be a no-op for the rest of the session");
164
+ }
165
+ state.errorCount += 1;
166
+ return;
167
+ }
168
+ state.linterAvailable = true;
169
+ state.probeComplete = true;
170
+ state.lastResult = {
171
+ path: filePath,
172
+ tool: toolName,
173
+ changed: result.changed,
174
+ bytesBefore: result.bytesBefore,
175
+ bytesAfter: result.bytesAfter,
176
+ when: (/* @__PURE__ */ new Date()).toISOString()
177
+ };
178
+ if (result.changed) {
179
+ state.organizedCount += 1;
180
+ const delta = result.bytesAfter - result.bytesBefore;
181
+ api.log.info(`import-organizer: reorganized imports in ${filePath}`, {
182
+ tool: toolName,
183
+ command: result.command,
184
+ delta: `${delta >= 0 ? "+" : ""}${delta} bytes`
185
+ });
186
+ return {
187
+ additionalContext: `
188
+ \u{1F4E6} import-organizer: organized imports in '${filePath}' after ${toolName}. Imports have been sorted, grouped, and unused imports removed (${delta >= 0 ? "+" : ""}${delta} bytes).`
189
+ };
190
+ }
191
+ state.cleanCount += 1;
192
+ if (result.stderr.trim().length > 0) {
193
+ return {
194
+ additionalContext: `
195
+ \u{1F4E6} import-organizer: '${filePath}' was already clean, but the linter reported:
196
+ ${result.stderr.trim()}`
197
+ };
198
+ }
199
+ return;
200
+ };
201
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook);
202
+ api.tools.register({
203
+ name: "import_organizer_status",
204
+ description: "Reports import-organizer state: linter availability, config, and per-session organized/clean/error counters.",
205
+ inputSchema: { type: "object", properties: {} },
206
+ permission: "auto",
207
+ category: "Code Quality",
208
+ mutating: false,
209
+ async execute() {
210
+ return {
211
+ ok: true,
212
+ enabled: cfg.enabled,
213
+ command: cfg.command,
214
+ fallbackCommand: cfg.fallbackCommand,
215
+ timeoutMs: cfg.timeoutMs,
216
+ linterAvailable: state.linterAvailable,
217
+ counters: {
218
+ invocations: state.invocationCount,
219
+ organized: state.organizedCount,
220
+ clean: state.cleanCount,
221
+ errors: state.errorCount
222
+ },
223
+ lastResult: state.lastResult
224
+ };
225
+ }
226
+ });
227
+ api.log.info("import-organizer plugin loaded", {
228
+ version: "0.1.0",
229
+ enabled: cfg.enabled,
230
+ command: cfg.command
231
+ });
232
+ },
233
+ teardown(api) {
234
+ if (state.hookUnregister) {
235
+ try {
236
+ state.hookUnregister();
237
+ } catch {
238
+ }
239
+ state.hookUnregister = null;
240
+ }
241
+ const final = {
242
+ invocations: state.invocationCount,
243
+ organized: state.organizedCount,
244
+ clean: state.cleanCount,
245
+ errors: state.errorCount
246
+ };
247
+ state.invocationCount = 0;
248
+ state.organizedCount = 0;
249
+ state.cleanCount = 0;
250
+ state.errorCount = 0;
251
+ state.lastResult = null;
252
+ state.probeComplete = false;
253
+ state.linterAvailable = false;
254
+ api.log.info("import-organizer: teardown complete", { final });
255
+ },
256
+ async health() {
257
+ const base = `import-organizer: ${state.invocationCount} invocation(s), ${state.organizedCount} organized, ${state.cleanCount} clean, ${state.errorCount} error(s)`;
258
+ const linterNote = state.probeComplete ? state.linterAvailable ? " (linter: ok)" : " (linter: missing)" : " (linter: not yet probed)";
259
+ return {
260
+ ok: true,
261
+ message: base + linterNote,
262
+ counters: {
263
+ invocations: state.invocationCount,
264
+ organized: state.organizedCount,
265
+ clean: state.cleanCount,
266
+ errors: state.errorCount
267
+ },
268
+ lastResult: state.lastResult
269
+ };
270
+ }
271
+ };
272
+ var import_organizer_default = plugin;
273
+
274
+ export { import_organizer_default as default };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,37 @@
1
1
  export { default as autoDocPlugin } from './auto-doc.js';
2
- export { default as gitAutocommitPlugin } from './git-autocommit.js';
3
- export { default as shellCheckPlugin } from './shell-check.js';
2
+ export { default as autoEscalatePlugin } from './auto-escalate.js';
3
+ export { default as branchGuardPlugin } from './branch-guard.js';
4
+ export { default as changelogWriterPlugin } from './changelog-writer.js';
5
+ export { default as checkpointPlugin } from './checkpoint.js';
6
+ export { default as commitValidatorPlugin } from './commit-validator.js';
7
+ export { default as configValidatorPlugin } from './config-validator.js';
8
+ export { default as contextPinsPlugin } from './context-pins.js';
4
9
  export { default as costTrackerPlugin } from './cost-tracker.js';
5
- export { default as fileWatcherPlugin } from './file-watcher.js';
6
- export { default as webSearchPlugin } from './web-search.js';
7
- export { default as jsonPathPlugin } from './json-path.js';
8
10
  export { default as cronPlugin } from './cron.js';
9
- export { default as templateEnginePlugin } from './template-engine.js';
11
+ export { default as depGuardPlugin } from './dep-guard.js';
12
+ export { default as diffSummaryPlugin } from './diff-summary.js';
13
+ export { default as errorLensPlugin } from './error-lens.js';
14
+ export { default as fileWatcherPlugin } from './file-watcher.js';
15
+ export { default as formatOnSavePlugin } from './format-on-save.js';
16
+ export { default as gitAutocommitPlugin } from './git-autocommit.js';
17
+ export { default as importOrganizerPlugin } from './import-organizer.js';
18
+ export { default as injectionShieldPlugin } from './injection-shield.js';
19
+ export { default as lintGatePlugin } from './lint-gate.js';
20
+ export { default as llmCachePlugin } from './llm-cache.js';
21
+ export { default as loopBreakerPlugin } from './loop-breaker.js';
22
+ export { default as modelRouterPlugin } from './model-router.js';
23
+ export { default as notifyHubPlugin } from './notify-hub.js';
24
+ export { default as pathGuardPlugin } from './path-guard.js';
25
+ export { default as promptFirewallPlugin } from './prompt-firewall.js';
26
+ export { default as secretScannerPlugin } from './secret-scanner.js';
10
27
  export { default as semverBumpPlugin } from './semver-bump.js';
28
+ export { default as sessionRecapPlugin } from './session-recap.js';
29
+ export { default as shellCheckPlugin } from './shell-check.js';
30
+ export { default as specLinkerPlugin } from './spec-linker.js';
31
+ export { default as templateEnginePlugin } from './template-engine.js';
32
+ export { default as testRunnerGatePlugin } from './test-runner-gate.js';
33
+ export { default as todoListenerPlugin } from './todo-listener.js';
34
+ export { default as todoTrackerPlugin } from './todo-tracker.js';
35
+ export { default as tokenBudgetPlugin } from './token-budget.js';
36
+ export { default as tokenThrottlePlugin } from './token-throttle.js';
11
37
  import '@wrongstack/core';