@wrongstack/plugins 0.260.0 → 0.265.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.
package/dist/auto-doc.js CHANGED
@@ -50,32 +50,7 @@ function parseSource(content) {
50
50
  }
51
51
  return entities;
52
52
  }
53
- function generateJSDoc(entity, includeTypes) {
54
- switch (entity.kind) {
55
- case "function": {
56
- const params = entity.params.map((p) => ` * @param ${p} - TODO: describe parameter`).join("\n");
57
- const returns = entity.returnType ? `
58
- * @returns ${includeTypes ? `{${entity.returnType}} ` : ""}TODO: describe return value` : "";
59
- return `/**
60
- * TODO: One-line description of ${entity.name}
61
- ${params}${returns}
62
- */`;
63
- }
64
- case "class":
65
- return `/**
66
- * TODO: Describe class ${entity.name}
67
- */`;
68
- case "type":
69
- return `/**
70
- * TODO: Describe type ${entity.name}
71
- */`;
72
- case "interface":
73
- return `/**
74
- * TODO: Describe interface ${entity.name}
75
- */`;
76
- }
77
- }
78
- function generateTSDoc(entity, includeTypes) {
53
+ function generateDocComment(entity, includeTypes) {
79
54
  switch (entity.kind) {
80
55
  case "function": {
81
56
  const params = entity.params.map((p) => ` * @param ${p} - TODO: describe parameter`).join("\n");
@@ -112,7 +87,7 @@ function injectDocComment(content, entity, doc) {
112
87
  const idx = entity.startLine - 1;
113
88
  const codeLine = lines[idx] ?? "";
114
89
  const indent = codeLine.match(/^(\s*)/)?.[1] ?? "";
115
- lines.splice(idx, 0, `${indent}${doc} ${codeLine.trim()}`);
90
+ lines.splice(idx, 0, `${indent}${doc}`);
116
91
  return lines.join("\n");
117
92
  }
118
93
  async function runAutoDoc(input, api) {
@@ -122,7 +97,6 @@ async function runAutoDoc(input, api) {
122
97
  if (input.files.length === 0) {
123
98
  return { ok: false, error: "input.files is empty \u2014 provide at least one file path", filesProcessed: 0, changes: [] };
124
99
  }
125
- const style = input.style ?? "tsdoc";
126
100
  const includeTypes = api.config.extensions?.["auto-doc"]?.["includeTypes"] ?? false;
127
101
  const results = [];
128
102
  for (const file of input.files) {
@@ -139,7 +113,7 @@ async function runAutoDoc(input, api) {
139
113
  let modified = content;
140
114
  for (const entity of entities) {
141
115
  if (!input.force && !needsDocComment(modified, entity)) continue;
142
- const doc = style === "jsdoc" ? generateJSDoc(entity, includeTypes) : generateTSDoc(entity, includeTypes);
116
+ const doc = generateDocComment(entity, includeTypes);
143
117
  modified = injectDocComment(modified, entity, doc);
144
118
  results.push({ file, entity: entity.name });
145
119
  }
@@ -160,7 +134,6 @@ async function runAutoDocPreview(input, api) {
160
134
  if (input.files.length === 0) {
161
135
  return { ok: false, error: "input.files is empty \u2014 provide at least one file path", previews: [] };
162
136
  }
163
- const style = input.style ?? "tsdoc";
164
137
  const includeTypes = api.config.extensions?.["auto-doc"]?.["includeTypes"] ?? false;
165
138
  const previews = [];
166
139
  for (const file of input.files) {
@@ -168,7 +141,7 @@ async function runAutoDocPreview(input, api) {
168
141
  const { readFileSync } = await import('fs');
169
142
  const content = readFileSync(file, "utf-8");
170
143
  const entities = parseSource(content);
171
- const generated = entities.filter((e) => !needsDocComment(content, e)).map((e) => style === "jsdoc" ? generateJSDoc(e, includeTypes) : generateTSDoc(e, includeTypes));
144
+ const generated = entities.filter((e) => needsDocComment(content, e)).map((e) => generateDocComment(e, includeTypes));
172
145
  previews.push({ file, entities: generated });
173
146
  } catch {
174
147
  api.log.warn(`auto-doc-preview: could not read file ${file}`);
@@ -164,9 +164,10 @@ var plugin = {
164
164
  const includeModel = input["includeModel"] ?? true;
165
165
  if (format === "csv") {
166
166
  const header = includeModel ? "model,timestamp,prompt_tokens,completion_tokens,total_tokens,cost_usd" : "timestamp,prompt_tokens,completion_tokens,total_tokens,cost_usd";
167
- const rows = sessionCost.requests.map(
168
- (r) => includeModel ? `${r.model},${r.timestamp},${r.promptTokens},${r.completionTokens},${r.totalTokens},${r.costUsd ?? 0}` : `${r.timestamp},${r.promptTokens},${r.completionTokens},${r.totalTokens},${r.costUsd ?? 0}`
169
- );
167
+ const rows = sessionCost.requests.map((r) => {
168
+ const cost = r.costUsd ?? 0;
169
+ return includeModel ? `${r.model},${r.timestamp},${r.promptTokens},${r.completionTokens},${r.totalTokens},${cost}` : `${r.timestamp},${r.promptTokens},${r.completionTokens},${r.totalTokens},${cost}`;
170
+ });
170
171
  return {
171
172
  ok: true,
172
173
  format: "csv",
@@ -136,11 +136,11 @@ var plugin = {
136
136
  }
137
137
  },
138
138
  setup(api) {
139
- const cwd = api.config.extensions?.["git-autocommit"];
139
+ const extConfig = api.config.extensions?.["git-autocommit"];
140
140
  const opts = {
141
- conventionalCommits: cwd?.["conventionalCommits"] ?? true,
142
- autoStage: cwd?.["autoStage"] ?? false,
143
- defaultType: cwd?.["defaultType"] ?? "feat"
141
+ conventionalCommits: extConfig?.["conventionalCommits"] ?? true,
142
+ autoStage: extConfig?.["autoStage"] ?? false,
143
+ defaultType: extConfig?.["defaultType"] ?? "feat"
144
144
  };
145
145
  api.tools.register({
146
146
  name: "git_autocommit",
@@ -273,6 +273,7 @@ ${stagedDiff}
273
273
  hash: String(hash),
274
274
  commitType: type,
275
275
  scope: String(scope ?? ""),
276
+ /* v8 ignore next -- staged is always an array here; the : [] fallback is defensive. */
276
277
  files: Array.isArray(staged) ? staged : [],
277
278
  warning: warning ?? null
278
279
  });
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ import { isAbsolute, join } from 'path';
5
5
  import { expectDefined, deepMerge as deepMerge$1, isPrivateIPv4, isPrivateIPv6 } from '@wrongstack/core';
6
6
  import { lookup } from 'dns/promises';
7
7
  import { isIPv4, isIPv6 } from 'net';
8
+ import { toErrorMessage } from '@wrongstack/core/utils';
8
9
 
9
10
  // src/auto-doc/index.ts
10
11
  var AUTO_DOC_API_VERSION = "^0.1.10";
@@ -58,32 +59,7 @@ function parseSource(content) {
58
59
  }
59
60
  return entities;
60
61
  }
61
- function generateJSDoc(entity, includeTypes) {
62
- switch (entity.kind) {
63
- case "function": {
64
- const params = entity.params.map((p) => ` * @param ${p} - TODO: describe parameter`).join("\n");
65
- const returns = entity.returnType ? `
66
- * @returns ${includeTypes ? `{${entity.returnType}} ` : ""}TODO: describe return value` : "";
67
- return `/**
68
- * TODO: One-line description of ${entity.name}
69
- ${params}${returns}
70
- */`;
71
- }
72
- case "class":
73
- return `/**
74
- * TODO: Describe class ${entity.name}
75
- */`;
76
- case "type":
77
- return `/**
78
- * TODO: Describe type ${entity.name}
79
- */`;
80
- case "interface":
81
- return `/**
82
- * TODO: Describe interface ${entity.name}
83
- */`;
84
- }
85
- }
86
- function generateTSDoc(entity, includeTypes) {
62
+ function generateDocComment(entity, includeTypes) {
87
63
  switch (entity.kind) {
88
64
  case "function": {
89
65
  const params = entity.params.map((p) => ` * @param ${p} - TODO: describe parameter`).join("\n");
@@ -120,7 +96,7 @@ function injectDocComment(content, entity, doc) {
120
96
  const idx = entity.startLine - 1;
121
97
  const codeLine = lines[idx] ?? "";
122
98
  const indent = codeLine.match(/^(\s*)/)?.[1] ?? "";
123
- lines.splice(idx, 0, `${indent}${doc} ${codeLine.trim()}`);
99
+ lines.splice(idx, 0, `${indent}${doc}`);
124
100
  return lines.join("\n");
125
101
  }
126
102
  async function runAutoDoc(input, api) {
@@ -130,7 +106,6 @@ async function runAutoDoc(input, api) {
130
106
  if (input.files.length === 0) {
131
107
  return { ok: false, error: "input.files is empty \u2014 provide at least one file path", filesProcessed: 0, changes: [] };
132
108
  }
133
- const style = input.style ?? "tsdoc";
134
109
  const includeTypes = api.config.extensions?.["auto-doc"]?.["includeTypes"] ?? false;
135
110
  const results = [];
136
111
  for (const file of input.files) {
@@ -147,7 +122,7 @@ async function runAutoDoc(input, api) {
147
122
  let modified = content;
148
123
  for (const entity of entities) {
149
124
  if (!input.force && !needsDocComment(modified, entity)) continue;
150
- const doc = style === "jsdoc" ? generateJSDoc(entity, includeTypes) : generateTSDoc(entity, includeTypes);
125
+ const doc = generateDocComment(entity, includeTypes);
151
126
  modified = injectDocComment(modified, entity, doc);
152
127
  results.push({ file, entity: entity.name });
153
128
  }
@@ -168,7 +143,6 @@ async function runAutoDocPreview(input, api) {
168
143
  if (input.files.length === 0) {
169
144
  return { ok: false, error: "input.files is empty \u2014 provide at least one file path", previews: [] };
170
145
  }
171
- const style = input.style ?? "tsdoc";
172
146
  const includeTypes = api.config.extensions?.["auto-doc"]?.["includeTypes"] ?? false;
173
147
  const previews = [];
174
148
  for (const file of input.files) {
@@ -176,7 +150,7 @@ async function runAutoDocPreview(input, api) {
176
150
  const { readFileSync: readFileSync2 } = await import('fs');
177
151
  const content = readFileSync2(file, "utf-8");
178
152
  const entities = parseSource(content);
179
- const generated = entities.filter((e) => !needsDocComment(content, e)).map((e) => style === "jsdoc" ? generateJSDoc(e, includeTypes) : generateTSDoc(e, includeTypes));
153
+ const generated = entities.filter((e) => needsDocComment(content, e)).map((e) => generateDocComment(e, includeTypes));
180
154
  previews.push({ file, entities: generated });
181
155
  } catch {
182
156
  api.log.warn(`auto-doc-preview: could not read file ${file}`);
@@ -377,11 +351,11 @@ var plugin2 = {
377
351
  }
378
352
  },
379
353
  setup(api) {
380
- const cwd = api.config.extensions?.["git-autocommit"];
354
+ const extConfig = api.config.extensions?.["git-autocommit"];
381
355
  const opts = {
382
- conventionalCommits: cwd?.["conventionalCommits"] ?? true,
383
- autoStage: cwd?.["autoStage"] ?? false,
384
- defaultType: cwd?.["defaultType"] ?? "feat"
356
+ conventionalCommits: extConfig?.["conventionalCommits"] ?? true,
357
+ autoStage: extConfig?.["autoStage"] ?? false,
358
+ defaultType: extConfig?.["defaultType"] ?? "feat"
385
359
  };
386
360
  api.tools.register({
387
361
  name: "git_autocommit",
@@ -514,6 +488,7 @@ ${stagedDiff}
514
488
  hash: String(hash),
515
489
  commitType: type,
516
490
  scope: String(scope ?? ""),
491
+ /* v8 ignore next -- staged is always an array here; the : [] fallback is defensive. */
517
492
  files: Array.isArray(staged) ? staged : [],
518
493
  warning: warning ?? null
519
494
  });
@@ -681,13 +656,8 @@ function runShellCheck(files, severity, cwd) {
681
656
  info: "info",
682
657
  style: "style"
683
658
  };
684
- const args = [
685
- "-f",
686
- "json",
687
- "-S",
688
- levelMap[severity] ?? "warning",
689
- ...files
690
- ];
659
+ const severityFlag = levelMap[severity] ?? "warning";
660
+ const args = ["-f", "json", "-S", severityFlag, ...files];
691
661
  let raw;
692
662
  try {
693
663
  raw = execFileSync("shellcheck", args, {
@@ -1054,9 +1024,10 @@ var plugin4 = {
1054
1024
  const includeModel = input["includeModel"] ?? true;
1055
1025
  if (format === "csv") {
1056
1026
  const header = includeModel ? "model,timestamp,prompt_tokens,completion_tokens,total_tokens,cost_usd" : "timestamp,prompt_tokens,completion_tokens,total_tokens,cost_usd";
1057
- const rows = sessionCost.requests.map(
1058
- (r) => includeModel ? `${r.model},${r.timestamp},${r.promptTokens},${r.completionTokens},${r.totalTokens},${r.costUsd ?? 0}` : `${r.timestamp},${r.promptTokens},${r.completionTokens},${r.totalTokens},${r.costUsd ?? 0}`
1059
- );
1027
+ const rows = sessionCost.requests.map((r) => {
1028
+ const cost = r.costUsd ?? 0;
1029
+ return includeModel ? `${r.model},${r.timestamp},${r.promptTokens},${r.completionTokens},${r.totalTokens},${cost}` : `${r.timestamp},${r.promptTokens},${r.completionTokens},${r.totalTokens},${cost}`;
1030
+ });
1060
1031
  return {
1061
1032
  ok: true,
1062
1033
  format: "csv",
@@ -1550,7 +1521,8 @@ var plugin6 = {
1550
1521
  }
1551
1522
  const deduplicated = [];
1552
1523
  for (const r of rawResults) {
1553
- const normalized = (r.url.split("?")[0] ?? r.url).split("#")[0] ?? r.url;
1524
+ const noQuery = r.url.split("?")[0] ?? r.url;
1525
+ const normalized = noQuery.split("#")[0] ?? r.url;
1554
1526
  if (!seenUrls.has(normalized) && r.url.startsWith("http")) {
1555
1527
  seenUrls.add(normalized);
1556
1528
  deduplicated.push(r);
@@ -1675,6 +1647,7 @@ function jmespathSearch(data, query) {
1675
1647
  return Number(itemVal) >= Number(cmpVal);
1676
1648
  case "<=":
1677
1649
  return Number(itemVal) <= Number(cmpVal);
1650
+ /* v8 ignore next -- op is constrained to the six operators by the filter regex; default is unreachable. */
1678
1651
  default:
1679
1652
  return true;
1680
1653
  }
@@ -1701,6 +1674,7 @@ function jmespathSearch(data, query) {
1701
1674
  if (data === null) return "null";
1702
1675
  if (Array.isArray(data)) return "array";
1703
1676
  return typeof data;
1677
+ /* v8 ignore next 2 -- fn is constrained to the four names by the function regex; default is unreachable. */
1704
1678
  default:
1705
1679
  return null;
1706
1680
  }
@@ -1743,7 +1717,9 @@ function validateJsonSchema(data, schema) {
1743
1717
  errors.push(`${path2}: above maximum ${s["maximum"]}`);
1744
1718
  }
1745
1719
  if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
1746
- value.forEach((item, i) => check(item, s["items"], `${path2}[${i}]`));
1720
+ for (let i = 0; i < value.length; i++) {
1721
+ check(value[i], s["items"], `${path2}[${i}]`);
1722
+ }
1747
1723
  }
1748
1724
  if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
1749
1725
  const props = s["properties"];
@@ -2154,12 +2130,12 @@ function expandLoops(template, variables) {
2154
2130
  }
2155
2131
  );
2156
2132
  }
2157
- function renderTemplate(template, variables) {
2133
+ function renderTemplate(template, variables, escapeHtml = true) {
2158
2134
  let result = template;
2159
2135
  result = expandConditionals(result, variables);
2160
2136
  result = expandLoops(result, variables);
2161
2137
  result = expandTemplate(result, variables);
2162
- {
2138
+ if (escapeHtml) {
2163
2139
  result = result.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
2164
2140
  }
2165
2141
  return result;
@@ -2192,6 +2168,7 @@ var plugin9 = {
2192
2168
  },
2193
2169
  setup(api) {
2194
2170
  const templates = /* @__PURE__ */ new Map();
2171
+ const autoEscapeHtml = api.config.extensions?.["template-engine"]?.["autoEscapeHtml"] ?? true;
2195
2172
  api.tools.register({
2196
2173
  name: "template_expand",
2197
2174
  description: "Expand a template string with variable substitution. Supports {{variable}}, {{#if var}}...{{/if}} conditionals, and {{#each items}}...{{/each}} loops.",
@@ -2224,7 +2201,7 @@ var plugin9 = {
2224
2201
  }
2225
2202
  let result;
2226
2203
  try {
2227
- result = raw ? renderTemplateRaw(template, variables) : renderTemplate(template, variables);
2204
+ result = raw ? renderTemplateRaw(template, variables) : renderTemplate(template, variables, autoEscapeHtml);
2228
2205
  } catch (err) {
2229
2206
  return { ok: false, error: String(err) };
2230
2207
  }
@@ -2288,7 +2265,7 @@ var plugin9 = {
2288
2265
  }
2289
2266
  let result;
2290
2267
  try {
2291
- result = raw ? renderTemplateRaw(content, variables) : renderTemplate(content, variables);
2268
+ result = raw ? renderTemplateRaw(content, variables) : renderTemplate(content, variables, autoEscapeHtml);
2292
2269
  } catch (err) {
2293
2270
  return { ok: false, error: `Template rendering failed: ${err}` };
2294
2271
  }
@@ -2584,7 +2561,7 @@ var plugin10 = {
2584
2561
  try {
2585
2562
  commits = getRecentCommits(lastTag, cwd);
2586
2563
  } catch (err) {
2587
- const msg = err instanceof Error ? err.message : String(err);
2564
+ const msg = toErrorMessage(err);
2588
2565
  return { ok: false, error: `Git error: ${msg}`, bumpPart: "patch" };
2589
2566
  }
2590
2567
  bumpPart = determineBump(commits);
@@ -2615,7 +2592,7 @@ var plugin10 = {
2615
2592
  windowsHide: true
2616
2593
  });
2617
2594
  } catch (err) {
2618
- const msg = err instanceof Error ? err.message : String(err);
2595
+ const msg = toErrorMessage(err);
2619
2596
  return { ok: false, error: `bump script failed: ${msg}` };
2620
2597
  }
2621
2598
  for (const rel of ["package.json", "package-lock.json", "src/lib/utils.ts", "index.html"]) {
package/dist/json-path.js CHANGED
@@ -61,6 +61,7 @@ function jmespathSearch(data, query) {
61
61
  return Number(itemVal) >= Number(cmpVal);
62
62
  case "<=":
63
63
  return Number(itemVal) <= Number(cmpVal);
64
+ /* v8 ignore next -- op is constrained to the six operators by the filter regex; default is unreachable. */
64
65
  default:
65
66
  return true;
66
67
  }
@@ -87,6 +88,7 @@ function jmespathSearch(data, query) {
87
88
  if (data === null) return "null";
88
89
  if (Array.isArray(data)) return "array";
89
90
  return typeof data;
91
+ /* v8 ignore next 2 -- fn is constrained to the four names by the function regex; default is unreachable. */
90
92
  default:
91
93
  return null;
92
94
  }
@@ -129,7 +131,9 @@ function validateJsonSchema(data, schema) {
129
131
  errors.push(`${path}: above maximum ${s["maximum"]}`);
130
132
  }
131
133
  if (Array.isArray(value) && s["items"] && Array.isArray(s["items"])) {
132
- value.forEach((item, i) => check(item, s["items"], `${path}[${i}]`));
134
+ for (let i = 0; i < value.length; i++) {
135
+ check(value[i], s["items"], `${path}[${i}]`);
136
+ }
133
137
  }
134
138
  if (typeof value === "object" && value !== null && !Array.isArray(value) && s["properties"]) {
135
139
  const props = s["properties"];
@@ -1,4 +1,5 @@
1
1
  import { expectDefined } from '@wrongstack/core';
2
+ import { toErrorMessage } from '@wrongstack/core/utils';
2
3
  import { execFileSync } from 'child_process';
3
4
  import { existsSync, readFileSync, writeFileSync, readdirSync } from 'fs';
4
5
  import { join } from 'path';
@@ -197,7 +198,7 @@ var plugin = {
197
198
  try {
198
199
  commits = getRecentCommits(lastTag, cwd);
199
200
  } catch (err) {
200
- const msg = err instanceof Error ? err.message : String(err);
201
+ const msg = toErrorMessage(err);
201
202
  return { ok: false, error: `Git error: ${msg}`, bumpPart: "patch" };
202
203
  }
203
204
  bumpPart = determineBump(commits);
@@ -228,7 +229,7 @@ var plugin = {
228
229
  windowsHide: true
229
230
  });
230
231
  } catch (err) {
231
- const msg = err instanceof Error ? err.message : String(err);
232
+ const msg = toErrorMessage(err);
232
233
  return { ok: false, error: `bump script failed: ${msg}` };
233
234
  }
234
235
  for (const rel of ["package.json", "package-lock.json", "src/lib/utils.ts", "index.html"]) {
@@ -18,13 +18,8 @@ function runShellCheck(files, severity, cwd) {
18
18
  info: "info",
19
19
  style: "style"
20
20
  };
21
- const args = [
22
- "-f",
23
- "json",
24
- "-S",
25
- levelMap[severity] ?? "warning",
26
- ...files
27
- ];
21
+ const severityFlag = levelMap[severity] ?? "warning";
22
+ const args = ["-f", "json", "-S", severityFlag, ...files];
28
23
  let raw;
29
24
  try {
30
25
  raw = execFileSync("shellcheck", args, {
@@ -34,12 +34,12 @@ function expandLoops(template, variables) {
34
34
  }
35
35
  );
36
36
  }
37
- function renderTemplate(template, variables) {
37
+ function renderTemplate(template, variables, escapeHtml = true) {
38
38
  let result = template;
39
39
  result = expandConditionals(result, variables);
40
40
  result = expandLoops(result, variables);
41
41
  result = expandTemplate(result, variables);
42
- {
42
+ if (escapeHtml) {
43
43
  result = result.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
44
44
  }
45
45
  return result;
@@ -72,6 +72,7 @@ var plugin = {
72
72
  },
73
73
  setup(api) {
74
74
  const templates = /* @__PURE__ */ new Map();
75
+ const autoEscapeHtml = api.config.extensions?.["template-engine"]?.["autoEscapeHtml"] ?? true;
75
76
  api.tools.register({
76
77
  name: "template_expand",
77
78
  description: "Expand a template string with variable substitution. Supports {{variable}}, {{#if var}}...{{/if}} conditionals, and {{#each items}}...{{/each}} loops.",
@@ -104,7 +105,7 @@ var plugin = {
104
105
  }
105
106
  let result;
106
107
  try {
107
- result = raw ? renderTemplateRaw(template, variables) : renderTemplate(template, variables);
108
+ result = raw ? renderTemplateRaw(template, variables) : renderTemplate(template, variables, autoEscapeHtml);
108
109
  } catch (err) {
109
110
  return { ok: false, error: String(err) };
110
111
  }
@@ -168,7 +169,7 @@ var plugin = {
168
169
  }
169
170
  let result;
170
171
  try {
171
- result = raw ? renderTemplateRaw(content, variables) : renderTemplate(content, variables);
172
+ result = raw ? renderTemplateRaw(content, variables) : renderTemplate(content, variables, autoEscapeHtml);
172
173
  } catch (err) {
173
174
  return { ok: false, error: `Template rendering failed: ${err}` };
174
175
  }
@@ -190,7 +190,8 @@ var plugin = {
190
190
  }
191
191
  const deduplicated = [];
192
192
  for (const r of rawResults) {
193
- const normalized = (r.url.split("?")[0] ?? r.url).split("#")[0] ?? r.url;
193
+ const noQuery = r.url.split("?")[0] ?? r.url;
194
+ const normalized = noQuery.split("#")[0] ?? r.url;
194
195
  if (!seenUrls.has(normalized) && r.url.startsWith("http")) {
195
196
  seenUrls.add(normalized);
196
197
  deduplicated.push(r);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/plugins",
3
- "version": "0.260.0",
3
+ "version": "0.265.1",
4
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",
5
5
  "license": "MIT",
6
6
  "author": "ECOSTACK TECHNOLOGY OÜ",
@@ -63,7 +63,7 @@
63
63
  "vitest": "^4.1.8"
64
64
  },
65
65
  "dependencies": {
66
- "@wrongstack/core": "0.260.0"
66
+ "@wrongstack/core": "0.265.1"
67
67
  },
68
68
  "scripts": {
69
69
  "build": "tsup",