@wrongstack/plugins 0.318.0 → 0.319.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/accessibility-auditor.js +21 -4
- package/dist/api-compatibility-gate.js +3 -1
- package/dist/auto-i18n-extractor.js +9 -3
- package/dist/changelog-writer.js +1 -4
- package/dist/code-metrics.js +3 -1
- package/dist/config-validator.js +6 -2
- package/dist/context-pins.js +4 -1
- package/dist/cost-tracker.js +3 -1
- package/dist/cron.js +8 -2
- package/dist/dep-guard.js +3 -1
- package/dist/dependency-vulnerability-gate.js +3 -1
- package/dist/diff-summary.js +9 -3
- package/dist/doc-sync-guard.js +6 -2
- package/dist/feature-flag-tracker.js +3 -1
- package/dist/format-on-save.js +3 -1
- package/dist/import-organizer.js +3 -1
- package/dist/index.js +342 -121
- package/dist/interface-contract-guard.js +3 -1
- package/dist/knowledge-graph.js +9 -4
- package/dist/license-audit-gate.js +15 -3
- package/dist/path-guard.js +8 -2
- package/dist/pr-drafter.js +3 -1
- package/dist/process-guard.js +3 -1
- package/dist/prompt-firewall.js +14 -4
- package/dist/refactor-suggester.js +3 -1
- package/dist/security-hotspot-scanner.js +21 -3
- package/dist/semantic-search-indexer/index.d.ts +26 -0
- package/dist/semantic-search-indexer.js +100 -12
- package/dist/semver-bump.js +17 -12
- package/dist/shell-check.js +7 -10
- package/dist/smart-rename.js +8 -6
- package/dist/spec-linker.js +3 -1
- package/dist/template-engine.js +18 -12
- package/dist/test-flake-detector.js +1 -4
- package/dist/test-generator.js +3 -1
- package/dist/test-runner-gate.js +27 -15
- package/dist/type-gate.js +3 -1
- package/package.json +5 -5
|
@@ -175,7 +175,12 @@ async function auditFile(filePath, projectRoot) {
|
|
|
175
175
|
);
|
|
176
176
|
}
|
|
177
177
|
if (!hasPrimaryLabel && ATTR_PLACEHOLDER.test(tag)) {
|
|
178
|
-
add(
|
|
178
|
+
add(
|
|
179
|
+
lineNo,
|
|
180
|
+
"low-contrast-placeholder",
|
|
181
|
+
"warning",
|
|
182
|
+
"<input> uses placeholder text (often low contrast and disappears on input)"
|
|
183
|
+
);
|
|
179
184
|
}
|
|
180
185
|
}
|
|
181
186
|
TAG_INPUT.lastIndex = 0;
|
|
@@ -186,14 +191,24 @@ async function auditFile(filePath, projectRoot) {
|
|
|
186
191
|
const hasAriaLabel = ATTR_ARIA_LABEL.test(tag);
|
|
187
192
|
const hasTitle = ATTR_TITLE.test(tag);
|
|
188
193
|
if (!hasText && !hasAriaLabel && !hasTitle) {
|
|
189
|
-
add(
|
|
194
|
+
add(
|
|
195
|
+
lineNo,
|
|
196
|
+
"missing-button-text",
|
|
197
|
+
"error",
|
|
198
|
+
"<button> has no visible text or accessible label"
|
|
199
|
+
);
|
|
190
200
|
}
|
|
191
201
|
}
|
|
192
202
|
TAG_BUTTON.lastIndex = 0;
|
|
193
203
|
for (const inputButtonMatch of line.matchAll(INPUT_BUTTON)) {
|
|
194
204
|
const tag = inputButtonMatch[0];
|
|
195
205
|
if (!ATTR_VALUE.test(tag) && !ATTR_ARIA_LABEL.test(tag) && !ATTR_TITLE.test(tag)) {
|
|
196
|
-
add(
|
|
206
|
+
add(
|
|
207
|
+
lineNo,
|
|
208
|
+
"missing-button-text",
|
|
209
|
+
"error",
|
|
210
|
+
`<input type="${inputButtonMatch[1]}"> is missing value/aria-label/title`
|
|
211
|
+
);
|
|
197
212
|
}
|
|
198
213
|
}
|
|
199
214
|
INPUT_BUTTON.lastIndex = 0;
|
|
@@ -328,7 +343,9 @@ var plugin = {
|
|
|
328
343
|
}
|
|
329
344
|
return { additionalContext: summary };
|
|
330
345
|
};
|
|
331
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
346
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
347
|
+
background: true
|
|
348
|
+
});
|
|
332
349
|
api.tools.register({
|
|
333
350
|
name: "a11y_audit",
|
|
334
351
|
description: "Audit a file or directory for accessibility issues. Scans .tsx/.jsx/.html/.vue files for missing alt text, missing labels, low-contrast placeholders, missing button text, and duplicate ids.",
|
|
@@ -284,7 +284,9 @@ var plugin = {
|
|
|
284
284
|
}
|
|
285
285
|
return { additionalContext: message };
|
|
286
286
|
};
|
|
287
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
287
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
288
|
+
background: true
|
|
289
|
+
});
|
|
288
290
|
api.tools.register({
|
|
289
291
|
name: "api_compat_status",
|
|
290
292
|
description: "Reports api-compatibility-gate state: severity, entry-point patterns, and per-session counters.",
|
|
@@ -80,7 +80,9 @@ function looksLikeUserText(value, minLength) {
|
|
|
80
80
|
if (value.length < minLength) return false;
|
|
81
81
|
if (/^\s*$/.test(value)) return false;
|
|
82
82
|
if (/^[0-9\s\W_]+$/.test(value)) return false;
|
|
83
|
-
if (/^(https?:|ftp:|www\.|\/|\.\/|@|#[0-9a-f]{3,8}$|\.?(jsx?|tsx?|vue|css|scss|json|png|svg|jpg)$)/i.test(
|
|
83
|
+
if (/^(https?:|ftp:|www\.|\/|\.\/|@|#[0-9a-f]{3,8}$|\.?(jsx?|tsx?|vue|css|scss|json|png|svg|jpg)$)/i.test(
|
|
84
|
+
value
|
|
85
|
+
)) {
|
|
84
86
|
return false;
|
|
85
87
|
}
|
|
86
88
|
return true;
|
|
@@ -88,7 +90,9 @@ function looksLikeUserText(value, minLength) {
|
|
|
88
90
|
function isExcludedContext(line, quoteIndex, excludeAttributes) {
|
|
89
91
|
const before = line.slice(0, quoteIndex);
|
|
90
92
|
for (const attr of excludeAttributes) {
|
|
91
|
-
const re = new RegExp(
|
|
93
|
+
const re = new RegExp(
|
|
94
|
+
`\\b${attr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*=\\s*(?:\\{\\s*)?["']?$`
|
|
95
|
+
);
|
|
92
96
|
if (re.test(before)) return true;
|
|
93
97
|
}
|
|
94
98
|
return false;
|
|
@@ -220,7 +224,9 @@ var plugin = {
|
|
|
220
224
|
${lines.join("\n")}${more}`;
|
|
221
225
|
return { additionalContext: context };
|
|
222
226
|
};
|
|
223
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
227
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
228
|
+
background: true
|
|
229
|
+
});
|
|
224
230
|
api.tools.register({
|
|
225
231
|
name: "i18n_extract",
|
|
226
232
|
description: "Scan a UI source file (.tsx/.jsx/.vue) for hardcoded user-facing string literals and return suggested i18n keys.",
|
package/dist/changelog-writer.js
CHANGED
|
@@ -386,10 +386,7 @@ var plugin = {
|
|
|
386
386
|
}
|
|
387
387
|
const rawInput = input ?? {};
|
|
388
388
|
const shouldPolish = input.polish === true || rawInput["use_llm"] === true || rawInput["useLlm"] === true || rawInput["ai"] === true || rawInput["use_ai"] === true;
|
|
389
|
-
const block = await maybePolish(
|
|
390
|
-
renderUnreleasedBlock(state.entries),
|
|
391
|
-
shouldPolish
|
|
392
|
-
);
|
|
389
|
+
const block = await maybePolish(renderUnreleasedBlock(state.entries), shouldPolish);
|
|
393
390
|
let existing = null;
|
|
394
391
|
try {
|
|
395
392
|
existing = readFileSync(cfg.filePath, "utf-8");
|
package/dist/code-metrics.js
CHANGED
|
@@ -241,7 +241,9 @@ var plugin = {
|
|
|
241
241
|
\u{1F4CA} code-metrics: ${relativePath(resolved)} \u2014 ${metrics.lines} lines (${metrics.codeLines} code, ${metrics.commentLines} comments, ${metrics.blankLines} blank), ${metrics.functionCount} function(s), complexity ${metrics.complexity}.`
|
|
242
242
|
};
|
|
243
243
|
};
|
|
244
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
244
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
245
|
+
background: true
|
|
246
|
+
});
|
|
245
247
|
api.tools.register({
|
|
246
248
|
name: "measure_code_metrics",
|
|
247
249
|
description: "Measure lines, comments, blank lines, function count, and cyclomatic-complexity-like score for a source file or directory.",
|
package/dist/config-validator.js
CHANGED
|
@@ -41,7 +41,9 @@ function readConfig(raw) {
|
|
|
41
41
|
const rawMax = r["maxFileBytes"] ?? r["max_file_bytes"] ?? r["maxBytes"] ?? r["max_bytes"];
|
|
42
42
|
return {
|
|
43
43
|
enabled: r["enabled"] !== false,
|
|
44
|
-
extensions: Array.isArray(rawExts) ? rawExts.filter((e) => typeof e === "string" && e.trim().length > 0).map(
|
|
44
|
+
extensions: Array.isArray(rawExts) ? rawExts.filter((e) => typeof e === "string" && e.trim().length > 0).map(
|
|
45
|
+
(e) => e.trim().startsWith(".") ? e.trim().toLowerCase() : `.${e.trim().toLowerCase()}`
|
|
46
|
+
) : [...DEFAULT_EXTENSIONS],
|
|
45
47
|
maxFileBytes: typeof rawMax === "number" && rawMax >= 1024 ? rawMax : DEFAULTS.maxFileBytes
|
|
46
48
|
};
|
|
47
49
|
}
|
|
@@ -335,7 +337,9 @@ var plugin = {
|
|
|
335
337
|
\u2026 and ${problems.length - 10} more` : "") + "\nFix these before moving on \u2014 downstream tools will fail on this file."
|
|
336
338
|
};
|
|
337
339
|
};
|
|
338
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
340
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
341
|
+
background: true
|
|
342
|
+
});
|
|
339
343
|
api.tools.register({
|
|
340
344
|
name: "config_validator_status",
|
|
341
345
|
description: "Reports config-validator state: watched extensions and counters (files checked, problems found).",
|
package/dist/context-pins.js
CHANGED
|
@@ -58,7 +58,10 @@ async function persistPins(filePath) {
|
|
|
58
58
|
if (!filePath) return true;
|
|
59
59
|
try {
|
|
60
60
|
await ensureDir(dirname(filePath));
|
|
61
|
-
await atomicWrite(
|
|
61
|
+
await atomicWrite(
|
|
62
|
+
filePath,
|
|
63
|
+
JSON.stringify({ pins: state.pins, nextId: state.nextId }, null, 2)
|
|
64
|
+
);
|
|
62
65
|
return true;
|
|
63
66
|
} catch {
|
|
64
67
|
state.persistErrors += 1;
|
package/dist/cost-tracker.js
CHANGED
|
@@ -191,7 +191,9 @@ var plugin = {
|
|
|
191
191
|
const rawCacheWrite = Number(u["cacheWrite"] ?? u["cache_creation_input_tokens"] ?? 0) || 0;
|
|
192
192
|
const freshTokens = rawInput + rawCacheWrite;
|
|
193
193
|
const promptTokens = freshTokens + cachedTokens;
|
|
194
|
-
const completionTokens = Number(
|
|
194
|
+
const completionTokens = Number(
|
|
195
|
+
u["output"] ?? u["completion_tokens"] ?? u["outputTokens"] ?? u["completionTokens"] ?? 0
|
|
196
|
+
) || 0;
|
|
195
197
|
const totalTokens = promptTokens + completionTokens;
|
|
196
198
|
const costUsd = estimateCost(model, freshTokens, completionTokens, cachedTokens);
|
|
197
199
|
const record = {
|
package/dist/cron.js
CHANGED
|
@@ -144,8 +144,14 @@ var plugin = {
|
|
|
144
144
|
type: "object",
|
|
145
145
|
properties: {
|
|
146
146
|
name: { type: "string", description: "Unique name for this cron job" },
|
|
147
|
-
intervalMs: {
|
|
148
|
-
|
|
147
|
+
intervalMs: {
|
|
148
|
+
type: "number",
|
|
149
|
+
description: "Interval between runs in milliseconds (minimum 1000)"
|
|
150
|
+
},
|
|
151
|
+
action: {
|
|
152
|
+
type: "string",
|
|
153
|
+
description: "Action identifier or description of what to run"
|
|
154
|
+
},
|
|
149
155
|
enabled: { type: "boolean", default: true }
|
|
150
156
|
},
|
|
151
157
|
required: ["name", "intervalMs", "action"]
|
package/dist/dep-guard.js
CHANGED
|
@@ -48,7 +48,9 @@ function parseInstallCommands(command) {
|
|
|
48
48
|
if (!cleaned) continue;
|
|
49
49
|
let name = cleaned;
|
|
50
50
|
let version = null;
|
|
51
|
-
const pipMatch = /^([A-Za-z0-9_.-]+(?:\[[^\]]+\])?)\s*(==|>=|<=|~=|!=|>|<)\s*(.+)$/.exec(
|
|
51
|
+
const pipMatch = /^([A-Za-z0-9_.-]+(?:\[[^\]]+\])?)\s*(==|>=|<=|~=|!=|>|<)\s*(.+)$/.exec(
|
|
52
|
+
cleaned
|
|
53
|
+
);
|
|
52
54
|
if (pipMatch?.[1]) {
|
|
53
55
|
name = pipMatch[1];
|
|
54
56
|
const op = pipMatch[2] ?? "";
|
|
@@ -71,7 +71,9 @@ function parseInstallCommands(command) {
|
|
|
71
71
|
if (!cleaned) continue;
|
|
72
72
|
let name = cleaned;
|
|
73
73
|
let version = null;
|
|
74
|
-
const pipMatch = /^([A-Za-z0-9_.-]+(?:\[[^\]]+\])?)\s*(==|>=|<=|~=|!=|>|<)\s*(.+)$/.exec(
|
|
74
|
+
const pipMatch = /^([A-Za-z0-9_.-]+(?:\[[^\]]+\])?)\s*(==|>=|<=|~=|!=|>|<)\s*(.+)$/.exec(
|
|
75
|
+
cleaned
|
|
76
|
+
);
|
|
75
77
|
if (pipMatch?.[1]) {
|
|
76
78
|
name = pipMatch[1];
|
|
77
79
|
const op = pipMatch[2] ?? "";
|
package/dist/diff-summary.js
CHANGED
|
@@ -199,8 +199,12 @@ var plugin = {
|
|
|
199
199
|
return;
|
|
200
200
|
}
|
|
201
201
|
const oldStr = String(inp["old_string"] ?? inp["TargetContent"] ?? inp["oldContent"] ?? "");
|
|
202
|
-
const newStr = String(
|
|
203
|
-
|
|
202
|
+
const newStr = String(
|
|
203
|
+
inp["new_string"] ?? inp["ReplacementContent"] ?? inp["newContent"] ?? ""
|
|
204
|
+
);
|
|
205
|
+
const contentStr = String(
|
|
206
|
+
inp["content"] ?? inp["CodeContent"] ?? inp["code"] ?? inp["text"] ?? inp["contents"] ?? inp["body"] ?? ""
|
|
207
|
+
);
|
|
204
208
|
const toolInputForHash = oldStr || newStr ? `${oldStr}:::${newStr}` : contentStr;
|
|
205
209
|
const now = Date.now();
|
|
206
210
|
const memo = pathMemo.get(filePath);
|
|
@@ -255,7 +259,9 @@ var plugin = {
|
|
|
255
259
|
additionalContext: header + summary
|
|
256
260
|
};
|
|
257
261
|
};
|
|
258
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
262
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
263
|
+
background: true
|
|
264
|
+
});
|
|
259
265
|
api.tools.register({
|
|
260
266
|
name: "diff_summary_status",
|
|
261
267
|
description: "Reports diff-summary state: mode, maxLines, and per-session invocation/injected/fallback counters.",
|
package/dist/doc-sync-guard.js
CHANGED
|
@@ -172,7 +172,9 @@ var plugin = {
|
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
if (!content || state.changedFiles.length === 0) return;
|
|
175
|
-
const missing = state.changedFiles.filter(
|
|
175
|
+
const missing = state.changedFiles.filter(
|
|
176
|
+
(changedPath) => !isReferenced(changedPath, content)
|
|
177
|
+
);
|
|
176
178
|
if (missing.length === 0) return;
|
|
177
179
|
state.warningsIssued += 1;
|
|
178
180
|
const missingList = missing.map((p) => ` - ${p}`).join("\n");
|
|
@@ -183,7 +185,9 @@ Consider mentioning these changes in the documentation.`;
|
|
|
183
185
|
return { additionalContext: message };
|
|
184
186
|
}
|
|
185
187
|
};
|
|
186
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
188
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
189
|
+
background: true
|
|
190
|
+
});
|
|
187
191
|
api.tools.register({
|
|
188
192
|
name: "doc_sync_status",
|
|
189
193
|
description: "Reports doc-sync-guard state: tracked changed files, doc-write count, and warning count.",
|
|
@@ -230,7 +230,9 @@ var plugin = {
|
|
|
230
230
|
Make sure flag behavior is intentional and consider updating flag inventory/docs.`
|
|
231
231
|
};
|
|
232
232
|
};
|
|
233
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
233
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
234
|
+
background: true
|
|
235
|
+
});
|
|
234
236
|
api.tools.register({
|
|
235
237
|
name: "scan_feature_flags",
|
|
236
238
|
description: "Scan source files for feature-flag-like expressions (isFeatureEnabled, featureFlags.*, useFeatureFlag, flags.*, plus custom patterns).",
|
package/dist/format-on-save.js
CHANGED
|
@@ -303,7 +303,9 @@ var plugin = {
|
|
|
303
303
|
state.cleanCount += 1;
|
|
304
304
|
return;
|
|
305
305
|
};
|
|
306
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
306
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
307
|
+
background: true
|
|
308
|
+
});
|
|
307
309
|
state.patternUnregister = api.onPattern(
|
|
308
310
|
"import-organizer:done",
|
|
309
311
|
(_eventName, payload) => {
|
package/dist/import-organizer.js
CHANGED
|
@@ -355,7 +355,9 @@ ${result.stderr.trim()}`
|
|
|
355
355
|
}
|
|
356
356
|
return;
|
|
357
357
|
};
|
|
358
|
-
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
358
|
+
state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
|
|
359
|
+
background: true
|
|
360
|
+
});
|
|
359
361
|
api.tools.register({
|
|
360
362
|
name: "import_organizer_status",
|
|
361
363
|
description: "Reports import-organizer state: linter availability, config, and per-session organized/clean/error counters.",
|