@wrongstack/plugins 0.319.0 → 1.0.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 (49) hide show
  1. package/dist/accessibility-auditor.js +21 -4
  2. package/dist/api-compatibility-gate.js +3 -1
  3. package/dist/auto-i18n-extractor.js +9 -3
  4. package/dist/changelog-writer.js +1 -4
  5. package/dist/checkpoint.js +40 -7
  6. package/dist/code-metrics.js +3 -1
  7. package/dist/config-validator.js +6 -2
  8. package/dist/context-pins.js +4 -1
  9. package/dist/cost-tracker.js +3 -1
  10. package/dist/cron.js +8 -2
  11. package/dist/dep-guard.js +3 -1
  12. package/dist/dependency-vulnerability-gate.js +3 -1
  13. package/dist/diff-summary.js +9 -3
  14. package/dist/doc-sync-guard.js +6 -2
  15. package/dist/feature-flag-tracker.js +3 -1
  16. package/dist/format-on-save.js +3 -1
  17. package/dist/import-organizer.js +3 -1
  18. package/dist/index.js +351 -152
  19. package/dist/interface-contract-guard.js +3 -1
  20. package/dist/knowledge-graph.js +9 -4
  21. package/dist/license-audit-gate.js +15 -3
  22. package/dist/migration-planner.js +1 -1
  23. package/dist/path-guard/index.d.ts +8 -1
  24. package/dist/path-guard.js +42 -16
  25. package/dist/pr-drafter.js +3 -1
  26. package/dist/process-guard.js +3 -1
  27. package/dist/prompt-firewall.js +16 -6
  28. package/dist/refactor-suggester.js +3 -1
  29. package/dist/release-notes-generator.js +1 -1
  30. package/dist/runtime/credential-patterns.d.ts +2 -2
  31. package/dist/runtime/h1-state.d.ts +2 -2
  32. package/dist/runtime/handles.d.ts +2 -2
  33. package/dist/runtime/index.d.ts +19 -2
  34. package/dist/runtime/llm.d.ts +2 -2
  35. package/dist/runtime/redos-guard.d.ts +2 -2
  36. package/dist/runtime/sandbox.d.ts +2 -2
  37. package/dist/secret-scanner.js +1 -1
  38. package/dist/security-hotspot-scanner.js +21 -3
  39. package/dist/semantic-search-indexer.js +12 -7
  40. package/dist/semver-bump.js +17 -12
  41. package/dist/shell-check.js +7 -10
  42. package/dist/smart-rename.js +8 -6
  43. package/dist/spec-linker.js +3 -1
  44. package/dist/template-engine.js +18 -12
  45. package/dist/test-flake-detector.js +1 -4
  46. package/dist/test-generator.js +4 -2
  47. package/dist/test-runner-gate.js +27 -15
  48. package/dist/type-gate.js +3 -1
  49. package/package.json +5 -5
@@ -200,7 +200,9 @@ var plugin = {
200
200
  If you changed member shapes, search the project for implementers/\`satisfies\`/\`as\` usages and update them accordingly.`
201
201
  };
202
202
  };
203
- state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
203
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
204
+ background: true
205
+ });
204
206
  api.tools.register({
205
207
  name: "check_interface_contracts",
206
208
  description: "Scan TypeScript files for interface declarations that have no visible implementer (implements / as / satisfies).",
@@ -240,9 +240,12 @@ var plugin = {
240
240
  const hasMatch = f.subject.toLowerCase().includes(q) || f.relation.toLowerCase().includes(q) || f.object.toLowerCase().includes(q);
241
241
  if (!hasMatch) return false;
242
242
  }
243
- if (input.subject && !f.subject.toLowerCase().includes(input.subject.toLowerCase())) return false;
244
- if (input.relation && !f.relation.toLowerCase().includes(input.relation.toLowerCase())) return false;
245
- if (input.object && !f.object.toLowerCase().includes(input.object.toLowerCase())) return false;
243
+ if (input.subject && !f.subject.toLowerCase().includes(input.subject.toLowerCase()))
244
+ return false;
245
+ if (input.relation && !f.relation.toLowerCase().includes(input.relation.toLowerCase()))
246
+ return false;
247
+ if (input.object && !f.object.toLowerCase().includes(input.object.toLowerCase()))
248
+ return false;
246
249
  if (filterConf && f.confidence.toLowerCase() !== filterConf) return false;
247
250
  return true;
248
251
  });
@@ -274,7 +277,9 @@ var plugin = {
274
277
  const raw = input ?? {};
275
278
  const rawId = String(input.id ?? raw["factId"] ?? raw["fact_id"] ?? "").trim();
276
279
  const normalized = rawId.toLowerCase().startsWith("kg-") ? rawId.toLowerCase() : `kg-${rawId.toLowerCase()}`;
277
- state.facts = state.facts.filter((f) => f.id.toLowerCase() !== normalized && f.id !== rawId);
280
+ state.facts = state.facts.filter(
281
+ (f) => f.id.toLowerCase() !== normalized && f.id !== rawId
282
+ );
278
283
  const removed = before - state.facts.length;
279
284
  if (removed === 0) return { ok: false, error: `no fact matches "${input.id ?? rawId}"` };
280
285
  state.removals += removed;
@@ -52,7 +52,9 @@ function parseInstallCommands(command) {
52
52
  if (!cleaned) continue;
53
53
  let name = cleaned;
54
54
  let version = null;
55
- const pipMatch = /^([A-Za-z0-9_.-]+(?:\[[^\]]+\])?)\s*(==|>=|<=|~=|!=|>|<)\s*(.+)$/.exec(cleaned);
55
+ const pipMatch = /^([A-Za-z0-9_.-]+(?:\[[^\]]+\])?)\s*(==|>=|<=|~=|!=|>|<)\s*(.+)$/.exec(
56
+ cleaned
57
+ );
56
58
  if (pipMatch?.[1]) {
57
59
  name = pipMatch[1];
58
60
  const op = pipMatch[2] ?? "";
@@ -391,7 +393,15 @@ var state2 = {
391
393
  };
392
394
  var DEFAULTS2 = {
393
395
  enabled: true,
394
- allowedLicenses: ["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC", "0BSD", "Unlicense"],
396
+ allowedLicenses: [
397
+ "MIT",
398
+ "Apache-2.0",
399
+ "BSD-2-Clause",
400
+ "BSD-3-Clause",
401
+ "ISC",
402
+ "0BSD",
403
+ "Unlicense"
404
+ ],
395
405
  block: true
396
406
  };
397
407
  function normalizeStrings(v) {
@@ -430,7 +440,9 @@ function extractLicenseStrings(pkg) {
430
440
  }
431
441
  function parsePackageNames(command) {
432
442
  return [
433
- ...new Set(parseInstallCommands(command).flatMap((entry) => entry.packages.map((pkg) => pkg.name)))
443
+ ...new Set(
444
+ parseInstallCommands(command).flatMap((entry) => entry.packages.map((pkg) => pkg.name))
445
+ )
434
446
  ];
435
447
  }
436
448
  function splitTopLevel(expr, separator) {
@@ -26,7 +26,7 @@ import {
26
26
  runOptionalPluginCouncil,
27
27
  runOptionalPluginLlm,
28
28
  stripOuterMarkdownFence
29
- } from "@wrongstack/plugin-sdk/runtime";
29
+ } from "@wrongstack/plugin-sdk/runtime/llm";
30
30
 
31
31
  // src/migration-planner/index.ts
32
32
  var API_VERSION = "^0.1.10";
@@ -7,7 +7,14 @@
7
7
  import type { Plugin } from '@wrongstack/core/types';
8
8
  export { compilePathGlob } from './glob.js';
9
9
  export { destructiveTargets } from './shell-targets.js';
10
- /** True when `path` is a project-local symlink whose real target escaped. */
10
+ /**
11
+ * True when `path` is a project-local path whose canonical resolution
12
+ * escaped the project — including when the leaf does not exist yet (e.g. a
13
+ * `write` of a brand-new file): the nearest EXISTING ancestor is
14
+ * canonicalized instead, so a new file created THROUGH an escaped symlink
15
+ * is still detected. A brand-new path under ordinary project directories
16
+ * resolves to an ancestor inside the project and returns false.
17
+ */
11
18
  export declare function isSymlinkEscape(path: string, cwd?: string): boolean;
12
19
  declare const plugin: Plugin;
13
20
  export default plugin;
@@ -12,11 +12,20 @@ var __copyProps = (to, from, except, desc) => {
12
12
  };
13
13
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
14
14
 
15
+ // src/path-guard/index.ts
16
+ import { existsSync, realpathSync } from "node:fs";
17
+ import { dirname, resolve } from "node:path";
18
+
19
+ // src/runtime/index.ts
20
+ var runtime_exports = {};
21
+ __reExport(runtime_exports, runtime_star);
22
+ import * as runtime_star from "@wrongstack/plugin-sdk/runtime";
23
+
15
24
  // src/runtime/redos-guard.ts
16
25
  import {
17
26
  withReDoSGuard,
18
27
  guardedMatcher
19
- } from "@wrongstack/plugin-sdk/runtime";
28
+ } from "@wrongstack/plugin-sdk/runtime/redos-guard";
20
29
 
21
30
  // src/path-guard/glob.ts
22
31
  var GLOB_REDOS_BUDGET_MS = 250;
@@ -1301,25 +1310,33 @@ function operationLabel(toolName) {
1301
1310
  return `write via "${toolName}"`;
1302
1311
  }
1303
1312
 
1304
- // src/path-guard/index.ts
1305
- import { realpathSync } from "node:fs";
1306
- import { resolve } from "node:path";
1307
-
1308
- // src/runtime/index.ts
1309
- var runtime_exports = {};
1310
- __reExport(runtime_exports, runtime_star);
1311
- import * as runtime_star from "@wrongstack/plugin-sdk/runtime";
1312
-
1313
1313
  // src/path-guard/index.ts
1314
1314
  function isSymlinkEscape(path, cwd) {
1315
1315
  if (!(0, runtime_exports.withinProject)(path)) return false;
1316
+ const abs = resolve(cwd ?? process.cwd(), path);
1317
+ let real;
1316
1318
  try {
1317
- const abs = resolve(cwd ?? process.cwd(), path);
1318
- const real = realpathSync(abs);
1319
- return !(0, runtime_exports.withinProject)(real);
1319
+ real = realpathSync(abs);
1320
1320
  } catch {
1321
- return false;
1321
+ const ancestor = nearestExistingAncestor(abs);
1322
+ if (ancestor === null) return false;
1323
+ try {
1324
+ real = realpathSync(ancestor);
1325
+ } catch {
1326
+ return false;
1327
+ }
1322
1328
  }
1329
+ return !(0, runtime_exports.withinProject)(real);
1330
+ }
1331
+ function nearestExistingAncestor(abs) {
1332
+ let current = dirname(abs);
1333
+ for (let hops = 0; hops < 64; hops++) {
1334
+ if (existsSync(current)) return current;
1335
+ const parent = dirname(current);
1336
+ if (parent === current) return null;
1337
+ current = parent;
1338
+ }
1339
+ return null;
1323
1340
  }
1324
1341
  function createState() {
1325
1342
  return {
@@ -1468,10 +1485,19 @@ var plugin = {
1468
1485
  path: relativeToInvocationCwd(resolveTargetPath(path, effectiveCwd), input.cwd),
1469
1486
  kind: isRootPathScope(path) && deletesImplicitScope || isUnresolvedPathScope(path) || recursivelyDeletes && (isDirectoryAmbiguousPath(path) || hasConfiguredProtectedDescendant(path, cfg.protect)) ? "deletion-scope" : "file"
1470
1487
  };
1488
+ if (target.kind === "file" && isSymlinkEscape(target.path, input.cwd)) {
1489
+ return target;
1490
+ }
1471
1491
  if (targetFullyAllowed(target, cfg.allow, allowRes)) return null;
1472
- const protectedShellTarget = await targetIntersectsPatternsGuarded(target, cfg.protect, protectRes, { onTimeout: bumpRedos }) || await matchesAnyGuarded(`${target.path.replace(/\/$/, "")}/.path-guard-probe`, protectRes, {
1492
+ const protectedShellTarget = await targetIntersectsPatternsGuarded(target, cfg.protect, protectRes, {
1473
1493
  onTimeout: bumpRedos
1474
- });
1494
+ }) || await matchesAnyGuarded(
1495
+ `${target.path.replace(/\/$/, "")}/.path-guard-probe`,
1496
+ protectRes,
1497
+ {
1498
+ onTimeout: bumpRedos
1499
+ }
1500
+ );
1475
1501
  return protectedShellTarget ? target : null;
1476
1502
  })
1477
1503
  );
@@ -286,7 +286,9 @@ var plugin = {
286
286
  async execute(input = {}) {
287
287
  if (!cfg.enabled) return { ok: false, error: "pr-drafter is disabled" };
288
288
  const raw = input ?? {};
289
- const preview = Boolean(input?.preview ?? raw["dryRun"] ?? raw["dry_run"] ?? raw["dry"] ?? raw["previewOnly"]);
289
+ const preview = Boolean(
290
+ input?.preview ?? raw["dryRun"] ?? raw["dry_run"] ?? raw["dry"] ?? raw["previewOnly"]
291
+ );
290
292
  const rawOutputPath = raw["outputPath"] ?? raw["output_path"] ?? raw["path"] ?? raw["filePath"] ?? raw["file"] ?? raw["TargetFile"] ?? raw["targetFile"] ?? cfg.outputPath;
291
293
  const outputPathStr = typeof rawOutputPath === "string" && rawOutputPath.trim().length > 0 ? rawOutputPath.trim() : cfg.outputPath;
292
294
  const draft = await buildDraft(cfg, api.llm);
@@ -64,7 +64,9 @@ var plugin = {
64
64
  const rawCmd = ti["command"] ?? ti["CommandLine"] ?? ti["cmd"] ?? ti["script"] ?? ti["input"];
65
65
  const command = typeof rawCmd === "string" ? rawCmd : "";
66
66
  if (!command) return;
67
- const isKillRelated = /\b(?:kill|taskkill|stop-process|tskill|pkill|killall|wmic)\b/i.test(command);
67
+ const isKillRelated = /\b(?:kill|taskkill|stop-process|tskill|pkill|killall|wmic)\b/i.test(
68
+ command
69
+ );
68
70
  if (!isKillRelated) return;
69
71
  state.detections += 1;
70
72
  state.lastDetection = {
@@ -5,13 +5,13 @@ import { performance } from "node:perf_hooks";
5
5
  import {
6
6
  cloneCredentialPatterns,
7
7
  CREDENTIAL_PATTERNS
8
- } from "@wrongstack/plugin-sdk/runtime";
8
+ } from "@wrongstack/plugin-sdk/runtime/credential-patterns";
9
9
 
10
10
  // src/runtime/redos-guard.ts
11
11
  import {
12
12
  withReDoSGuard,
13
13
  guardedMatcher
14
- } from "@wrongstack/plugin-sdk/runtime";
14
+ } from "@wrongstack/plugin-sdk/runtime/redos-guard";
15
15
 
16
16
  // src/prompt-firewall/index.ts
17
17
  var KIND_ALIASES = {
@@ -293,9 +293,12 @@ function surfaceScanTrips(api, tripped) {
293
293
  }
294
294
  state.timeoutCount += 1;
295
295
  api.metrics.counter("redos_skips", 1);
296
- api.log.warn("prompt-firewall: scan-pass budget exceeded \u2014 patterns skipped mid-pass (issue #370)", {
297
- skipped: [...tripped]
298
- });
296
+ api.log.warn(
297
+ "prompt-firewall: scan-pass budget exceeded \u2014 patterns skipped mid-pass (issue #370)",
298
+ {
299
+ skipped: [...tripped]
300
+ }
301
+ );
299
302
  }
300
303
  var plugin = {
301
304
  name: "prompt-firewall",
@@ -397,7 +400,14 @@ var plugin = {
397
400
  if (cfg.mode === "redact") {
398
401
  const counter = { n: 0 };
399
402
  const deadline = createScanDeadline();
400
- const redactedReq = redactDeep(req, cfg.allow, counter, skipSet, void 0, deadline);
403
+ const redactedReq = redactDeep(
404
+ req,
405
+ cfg.allow,
406
+ counter,
407
+ skipSet,
408
+ void 0,
409
+ deadline
410
+ );
401
411
  state.requestRedactions += counter.n;
402
412
  api.metrics.counter("request_redactions", counter.n);
403
413
  if (deadline.tripped.size > 0) surfaceScanTrips(api, deadline.tripped);
@@ -295,7 +295,9 @@ var plugin = {
295
295
  contextAs: "separate"
296
296
  };
297
297
  };
298
- state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
298
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
299
+ background: true
300
+ });
299
301
  api.tools.register({
300
302
  name: "suggest_refactors",
301
303
  description: "Scan source files for refactoring smells: long functions, deep nesting, many parameters, magic numbers, and console logging.",
@@ -7,7 +7,7 @@ import {
7
7
  runOptionalPluginCouncil,
8
8
  runOptionalPluginLlm,
9
9
  stripOuterMarkdownFence
10
- } from "@wrongstack/plugin-sdk/runtime";
10
+ } from "@wrongstack/plugin-sdk/runtime/llm";
11
11
 
12
12
  // src/release-notes-generator/index.ts
13
13
  var API_VERSION = "^0.1.10";
@@ -1,3 +1,3 @@
1
- /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime. */
2
- export { cloneCredentialPatterns, CREDENTIAL_PATTERNS, type CredentialPattern, } from '@wrongstack/plugin-sdk/runtime';
1
+ /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime/credential-patterns (granular). */
2
+ export { cloneCredentialPatterns, CREDENTIAL_PATTERNS, type CredentialPattern, } from '@wrongstack/plugin-sdk/runtime/credential-patterns';
3
3
  //# sourceMappingURL=credential-patterns.d.ts.map
@@ -1,3 +1,3 @@
1
- /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime. */
2
- export { createH1State, type H1State } from '@wrongstack/plugin-sdk/runtime';
1
+ /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime/h1-state (granular). */
2
+ export { createH1State, type H1State } from '@wrongstack/plugin-sdk/runtime/h1-state';
3
3
  //# sourceMappingURL=h1-state.d.ts.map
@@ -1,3 +1,3 @@
1
- /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime. */
2
- export { releaseHandle, releaseHandles, type Unregister } from '@wrongstack/plugin-sdk/runtime';
1
+ /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime/handles (granular). */
2
+ export { releaseHandle, releaseHandles, type Unregister } from '@wrongstack/plugin-sdk/runtime/handles';
3
3
  //# sourceMappingURL=handles.d.ts.map
@@ -1,10 +1,27 @@
1
1
  /**
2
2
  * Backwards-compatible re-export: the runtime helpers moved to
3
3
  * `@wrongstack/plugin-sdk/runtime` so third-party plugin authors get the
4
- * same audit-hardened helpers first-party plugins use, without depending
5
- * on the whole @wrongstack/plugins package. This shim keeps the
4
+ * same audit-hardened helpers first-party plugins use, without depending on
5
+ * the whole @wrongstack/plugins package. This shim keeps the
6
6
  * `@wrongstack/plugins/runtime` subpath (and every in-repo relative
7
7
  * import) working unchanged.
8
+ *
9
+ * Why this one still imports the BARREL while the six leaf shims
10
+ * (`redos-guard`, `sandbox`, `llm`, …) import granular
11
+ * `@wrongstack/plugin-sdk/runtime/*` entries: the language-runner helpers
12
+ * (`resolveRunnerCommand`, `sanitizeRunnerPath`, `runRunnerCommand`,
13
+ * `probeRunnerCommand`), `withinProject`, `collectSourceFiles(Async)` and
14
+ * `matchesExtension` are defined in the sdk's runtime barrel module itself.
15
+ *
16
+ * The runner-extraction unlock LANDED 2026-09-06 (round core-utils-r1):
17
+ * `@wrongstack/core/utils/child-env` imports in ~4.5ms and the plugin-sdk
18
+ * barrel uses it. The extraction was re-attempted as round sdk-runner-r2
19
+ * and REVERTED on measurement: the primary arm's win landed inside the
20
+ * noise band (path-guard 12.33 -> 11.46ms, band ~5.4ms) because the
21
+ * child-env entry already banked the barrel's cost, and the granular
22
+ * 10-resolution shim measurably regressed multi-symbol consumers
23
+ * (branch-guard 10.87 -> 16.95ms). Do not re-attempt without a new
24
+ * hypothesis; see PERF_LOG round sdk-runner-r2.
8
25
  */
9
26
  export * from '@wrongstack/plugin-sdk/runtime';
10
27
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,3 @@
1
- /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime. */
2
- export { parseLlmJsonObject, runOptionalPluginCouncil, runOptionalPluginLlm, stripOuterMarkdownFence, type OptionalCouncilRequest, type OptionalLlmRequest, type OptionalLlmResult, } from '@wrongstack/plugin-sdk/runtime';
1
+ /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime/llm (granular). */
2
+ export { parseLlmJsonObject, runOptionalPluginCouncil, runOptionalPluginLlm, stripOuterMarkdownFence, type OptionalCouncilRequest, type OptionalLlmRequest, type OptionalLlmResult, } from '@wrongstack/plugin-sdk/runtime/llm';
3
3
  //# sourceMappingURL=llm.d.ts.map
@@ -1,3 +1,3 @@
1
- /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime. */
2
- export { withReDoSGuard, guardedMatcher, type ReDoSResult, type ReDoSOptions, } from '@wrongstack/plugin-sdk/runtime';
1
+ /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime/redos-guard (granular). */
2
+ export { withReDoSGuard, guardedMatcher, type ReDoSResult, type ReDoSOptions, } from '@wrongstack/plugin-sdk/runtime/redos-guard';
3
3
  //# sourceMappingURL=redos-guard.d.ts.map
@@ -1,3 +1,3 @@
1
- /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime. */
2
- export { safePath, isInsideProject, type SafePathOptions, } from '@wrongstack/plugin-sdk/runtime';
1
+ /** Shim: implementation moved to @wrongstack/plugin-sdk/runtime/sandbox (granular). */
2
+ export { safePath, isInsideProject, type SafePathOptions, } from '@wrongstack/plugin-sdk/runtime/sandbox';
3
3
  //# sourceMappingURL=sandbox.d.ts.map
@@ -16,7 +16,7 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
16
16
  import {
17
17
  cloneCredentialPatterns,
18
18
  CREDENTIAL_PATTERNS
19
- } from "@wrongstack/plugin-sdk/runtime";
19
+ } from "@wrongstack/plugin-sdk/runtime/credential-patterns";
20
20
 
21
21
  // src/runtime/index.ts
22
22
  var runtime_exports = {};
@@ -38,7 +38,19 @@ var DEFAULTS = {
38
38
  enabled: false,
39
39
  severity: "warn",
40
40
  maxFindings: 10,
41
- scanOnChange: [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".py", ".java", ".go", ".rb", ".php"]
41
+ scanOnChange: [
42
+ ".js",
43
+ ".jsx",
44
+ ".ts",
45
+ ".tsx",
46
+ ".mjs",
47
+ ".cjs",
48
+ ".py",
49
+ ".java",
50
+ ".go",
51
+ ".rb",
52
+ ".php"
53
+ ]
42
54
  };
43
55
  var scanOnChangeSet = new Set(DEFAULTS.scanOnChange);
44
56
  function readConfig(raw) {
@@ -61,7 +73,11 @@ function readConfig(raw) {
61
73
  }
62
74
  var PATTERNS = [
63
75
  { type: "eval_call", severity: "high", regex: /\beval\s*\(/i },
64
- { type: "function_constructor", severity: "high", regex: /\bnew\s+Function\s*\(|\bFunction\s*\(/i },
76
+ {
77
+ type: "function_constructor",
78
+ severity: "high",
79
+ regex: /\bnew\s+Function\s*\(|\bFunction\s*\(/i
80
+ },
65
81
  {
66
82
  type: "unsafe_html",
67
83
  severity: "high",
@@ -299,7 +315,9 @@ Review or remove the risky pattern(s).`;
299
315
  }
300
316
  return { additionalContext: message };
301
317
  };
302
- state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, { background: true });
318
+ state.hookUnregister = api.registerHook("PostToolUse", "write|edit", hook, {
319
+ background: true
320
+ });
303
321
  api.tools.register({
304
322
  name: "security_hotspot_scan",
305
323
  description: "Scan a file or directory for security anti-patterns (eval, Function constructor, innerHTML, SQL concatenation, http URLs, credential logging, variable exec commands).",
@@ -52,11 +52,7 @@ var DEFAULTS = {
52
52
  ".scss",
53
53
  ".html"
54
54
  ],
55
- excludePatterns: [
56
- ...DEFAULT_WALK_IGNORE_DIRS.map(escapeRegex),
57
- "\\.wrongstack",
58
- "\\.temp_files"
59
- ],
55
+ excludePatterns: [...DEFAULT_WALK_IGNORE_DIRS.map(escapeRegex), "\\.wrongstack", "\\.temp_files"],
60
56
  maxFileBytes: 1e6,
61
57
  defaultLimit: 10,
62
58
  minTokenLength: 2,
@@ -64,7 +60,12 @@ var DEFAULTS = {
64
60
  maxFiles: 5e3
65
61
  };
66
62
  function readConfig(raw) {
67
- if (!raw || typeof raw !== "object") return { ...DEFAULTS, includeExtensions: [...DEFAULTS.includeExtensions], excludePatterns: [...DEFAULTS.excludePatterns] };
63
+ if (!raw || typeof raw !== "object")
64
+ return {
65
+ ...DEFAULTS,
66
+ includeExtensions: [...DEFAULTS.includeExtensions],
67
+ excludePatterns: [...DEFAULTS.excludePatterns]
68
+ };
68
69
  const r = raw;
69
70
  const rawExts = r["includeExtensions"] ?? r["include_extensions"] ?? r["extensions"] ?? r["file_extensions"];
70
71
  const includeExtensions = Array.isArray(rawExts) ? rawExts.filter((x) => typeof x === "string") : [...DEFAULTS.includeExtensions];
@@ -375,7 +376,11 @@ var plugin = {
375
376
  description: "Builds an in-memory keyword index over project source files and answers ranked search queries",
376
377
  apiVersion: API_VERSION,
377
378
  capabilities: { tools: true },
378
- defaultConfig: { ...DEFAULTS, includeExtensions: [...DEFAULTS.includeExtensions], excludePatterns: [...DEFAULTS.excludePatterns] },
379
+ defaultConfig: {
380
+ ...DEFAULTS,
381
+ includeExtensions: [...DEFAULTS.includeExtensions],
382
+ excludePatterns: [...DEFAULTS.excludePatterns]
383
+ },
379
384
  configSchema: {
380
385
  type: "object",
381
386
  properties: {
@@ -23,19 +23,24 @@ var state = {
23
23
  };
24
24
  function runCommand(command, args, cwd) {
25
25
  return new Promise((resolve2, reject) => {
26
- execFile(command, args, {
27
- encoding: "utf-8",
28
- cwd,
29
- timeout: 3e4,
30
- windowsHide: true
31
- }, (err, stdout, stderr) => {
32
- if (!err) {
33
- resolve2(stdout.trim());
34
- return;
26
+ execFile(
27
+ command,
28
+ args,
29
+ {
30
+ encoding: "utf-8",
31
+ cwd,
32
+ timeout: 3e4,
33
+ windowsHide: true
34
+ },
35
+ (err, stdout, stderr) => {
36
+ if (!err) {
37
+ resolve2(stdout.trim());
38
+ return;
39
+ }
40
+ const failure = err;
41
+ reject(Object.assign(failure, { stderr: stderr || void 0 }));
35
42
  }
36
- const failure = err;
37
- reject(Object.assign(failure, { stderr: stderr || void 0 }));
38
- });
43
+ );
39
44
  });
40
45
  }
41
46
  async function runGit(args, cwd) {
@@ -26,15 +26,10 @@ var state = {
26
26
  async function runShellCheck(files, severity, cwd) {
27
27
  try {
28
28
  await new Promise((resolvePromise, rejectPromise) => {
29
- execFile(
30
- "shellcheck",
31
- ["--version"],
32
- { encoding: "utf-8", windowsHide: true },
33
- (err) => {
34
- if (err) rejectPromise(err);
35
- else resolvePromise();
36
- }
37
- );
29
+ execFile("shellcheck", ["--version"], { encoding: "utf-8", windowsHide: true }, (err) => {
30
+ if (err) rejectPromise(err);
31
+ else resolvePromise();
32
+ });
38
33
  });
39
34
  } catch {
40
35
  throw new Error(
@@ -195,7 +190,9 @@ var plugin = {
195
190
  if (typeof rawFiles === "string" && rawFiles.trim().length > 0) {
196
191
  files = [rawFiles.trim()];
197
192
  } else if (Array.isArray(rawFiles)) {
198
- files = rawFiles.filter((f) => typeof f === "string" && f.trim().length > 0);
193
+ files = rawFiles.filter(
194
+ (f) => typeof f === "string" && f.trim().length > 0
195
+ );
199
196
  }
200
197
  }
201
198
  const rawDirectory = inp.directory ?? input["dir"] ?? input["SearchDirectory"];
@@ -15,7 +15,8 @@ function normalizeExtensions(exts) {
15
15
  return exts.map((e) => e.startsWith(".") ? e.toLowerCase() : `.${e.toLowerCase()}`);
16
16
  }
17
17
  function readConfig(raw) {
18
- if (!raw || typeof raw !== "object") return { ...DEFAULTS, extensions: normalizeExtensions(DEFAULTS.extensions) };
18
+ if (!raw || typeof raw !== "object")
19
+ return { ...DEFAULTS, extensions: normalizeExtensions(DEFAULTS.extensions) };
19
20
  const r = raw;
20
21
  const rawExts = r["extensions"] ?? r["file_extensions"] ?? r["fileExtensions"];
21
22
  const exts = Array.isArray(rawExts) ? rawExts.filter((x) => typeof x === "string") : DEFAULTS.extensions;
@@ -48,10 +49,7 @@ function isIdentifier(name) {
48
49
  return IDENTIFIER_RE.test(name);
49
50
  }
50
51
  function renameInContent(content, oldName, newName) {
51
- const re = new RegExp(
52
- `(?<![A-Za-z0-9_$])${escapeRegex(oldName)}(?![A-Za-z0-9_$])`,
53
- "g"
54
- );
52
+ const re = new RegExp(`(?<![A-Za-z0-9_$])${escapeRegex(oldName)}(?![A-Za-z0-9_$])`, "g");
55
53
  let replacements = 0;
56
54
  const preview = content.replace(re, () => {
57
55
  replacements++;
@@ -92,7 +90,11 @@ var plugin = {
92
90
  path: { type: "string", description: "Source file path (relative to project root)." },
93
91
  oldName: { type: "string", description: "Identifier to replace." },
94
92
  newName: { type: "string", description: "New identifier." },
95
- apply: { type: "boolean", default: false, description: "When true, write the preview back to disk." }
93
+ apply: {
94
+ type: "boolean",
95
+ default: false,
96
+ description: "When true, write the preview back to disk."
97
+ }
96
98
  },
97
99
  required: ["path", "oldName", "newName"]
98
100
  },
@@ -434,7 +434,9 @@ var plugin = {
434
434
  ${lines}${overflowNote}`
435
435
  };
436
436
  };
437
- state.postHookUnregister = api.registerHook("PostToolUse", "write|edit", postHook, { background: true });
437
+ state.postHookUnregister = api.registerHook("PostToolUse", "write|edit", postHook, {
438
+ background: true
439
+ });
438
440
  if (cfg.autoFix) {
439
441
  const preHook = async (input) => {
440
442
  if (!cfg.enabled) return;
@@ -43,21 +43,27 @@ function expandTemplate(template, variables) {
43
43
  return result;
44
44
  }
45
45
  function expandConditionals(template, variables) {
46
- return template.replace(/\{\{#if\s+([\w.-]+)\s*\}\}([\s\S]*?)\{\{\/if\}\}/g, (_, key, content) => {
47
- const val = variables[key];
48
- return val !== void 0 && val !== "" && val !== "false" && val !== "0" ? content : "";
49
- });
46
+ return template.replace(
47
+ /\{\{#if\s+([\w.-]+)\s*\}\}([\s\S]*?)\{\{\/if\}\}/g,
48
+ (_, key, content) => {
49
+ const val = variables[key];
50
+ return val !== void 0 && val !== "" && val !== "false" && val !== "0" ? content : "";
51
+ }
52
+ );
50
53
  }
51
54
  function expandLoops(template, variables) {
52
- return template.replace(/\{\{#each\s+([\w.-]+)\s*\}\}([\s\S]*?)\{\{\/each\}\}/g, (_, key, content) => {
53
- const val = variables[key];
54
- if (!val) return "";
55
- if (typeof val === "string" && val.includes(",")) {
56
- const items = val.split(",").map((s) => s.trim());
57
- return items.map((item) => expandTemplate(content, { ...variables, [key]: item, item })).join("\n");
55
+ return template.replace(
56
+ /\{\{#each\s+([\w.-]+)\s*\}\}([\s\S]*?)\{\{\/each\}\}/g,
57
+ (_, key, content) => {
58
+ const val = variables[key];
59
+ if (!val) return "";
60
+ if (typeof val === "string" && val.includes(",")) {
61
+ const items = val.split(",").map((s) => s.trim());
62
+ return items.map((item) => expandTemplate(content, { ...variables, [key]: item, item })).join("\n");
63
+ }
64
+ return expandTemplate(content, variables);
58
65
  }
59
- return expandTemplate(content, variables);
60
- });
66
+ );
61
67
  }
62
68
  function renderTemplate(template, variables, escapeHtml = true) {
63
69
  let result = template;
@@ -283,10 +283,7 @@ var plugin = {
283
283
  const commandString = typeof rawCommand === "string" && rawCommand.trim().length > 0 ? rawCommand.trim() : cfg.defaultCommand;
284
284
  const rawRuns = input.runs ?? raw["runsRequested"] ?? raw["count"] ?? raw["repeat"] ?? raw["times"];
285
285
  const requestedRuns = typeof rawRuns === "number" && rawRuns >= 1 ? Math.min(Math.floor(rawRuns), cfg.maxRuns) : 5;
286
- const command = resolveTestCommand(
287
- commandString,
288
- testPattern
289
- );
286
+ const command = resolveTestCommand(commandString, testPattern);
290
287
  if (!command) {
291
288
  return {
292
289
  ok: false,