ai-text-check 1.1.0 → 1.2.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.
package/README.md CHANGED
@@ -23,6 +23,9 @@ Node.js 20+ is required. The package ships dual **ESM and CommonJS** builds with
23
23
  | **is-this-ai-slop** | Clichés, buzzwords, em-dash spam, antithesis, sycophancy |
24
24
  | **veldica** | Stock LLM transitions and AI-style markers |
25
25
  | **clean-writing** | Banned constructions, marketing adjectives, filler, intensifiers |
26
+ | **ai-slop-linter** | Wikipedia _Signs of AI writing_ rules with line/column findings |
27
+ | **slop-detector** | EQBench SLOP score (vocabulary, contrast patterns, trigrams, tropes) |
28
+ | **prose-slop** | Vocabulary, punctuation, and document-cadence tells (English) |
26
29
  | **vale** | Vendored styles: write-good, Google, AiTextCheck, ai-tells, signs-of-ai-writing |
27
30
 
28
31
  Safe auto-fixes can strip filler openers and collapse extra whitespace without rewriting code fences.
@@ -146,6 +149,9 @@ Exit code `1` when issues meet the fail threshold.
146
149
  "isThisAiSlop": true,
147
150
  "veldica": true,
148
151
  "cleanWriting": true,
152
+ "aiSlopLinter": true,
153
+ "slopDetector": true,
154
+ "proseSlop": true,
149
155
  "vale": true
150
156
  }
151
157
  }
@@ -155,7 +161,7 @@ Presets:
155
161
 
156
162
  - `default`: every check and every engine
157
163
  - `strict`: default plus extra slop-gate punctuation
158
- - `ai`: AI tells + typography (polish, slop-gate, is-this-ai-slop, Veldica, clean-writing, Vale)
164
+ - `ai`: AI tells + typography (polish, slop-gate, is-this-ai-slop, Veldica, clean-writing, ai-slop-linter, slop-detector, prose-slop, Vale)
159
165
  - `grammar`: grammar + readability (write-good, retext, clean-writing, Vale)
160
166
 
161
167
  ## Scripts
package/SOURCES.md CHANGED
@@ -18,17 +18,22 @@ API key. Vale (`@vvago/vale`) is the prose engine the user referred to as Vale
18
18
  | is-this-ai-slop | `is-this-ai-slop` (`analyze()`) |
19
19
  | Veldica AI markers | `@veldica/prose-linter` (`inventoryMarkers()`) |
20
20
  | Clean Writing / STE subset | `clean-writing-lint` (`lint()`), with contraction and 20-word sentence rules off |
21
+ | ai-slop-linter | `ai-slop-linter` (`lintText()`) — Wikipedia Signs of AI writing |
22
+ | slop-detector (EQBench) | `slop-detector` scoring assets + sync adapter (`runSlopDetector`) |
23
+ | prose-slop | `prose-slop` (`check()`) — EN vocabulary / cadence |
21
24
 
22
25
  Skipped after research (not a fit for Node 20 in-process checks):
23
26
 
24
27
  - `slopsift` / WritingLint — requires Node 24+
25
- - `slopless` — textlint-only, async
28
+ - `slopless` — textlint-only, async, Node ≥22.13
26
29
  - `wsc-lint` / `tacheles` — CLI-only, no stable in-process API
30
+ - `smellcheck` (npm) — agent skill / Markdown rules only; does not scan text
31
+ - `llm-slop-detector` — VS Code–centric, awkward as a library dependency
27
32
  - Hosted classifiers (GPTZero, Originality, etc.) — need secrets / upload text
28
33
 
29
34
  Default `checkFiles` order: polish + slop-gate JSON + write-good + retext +
30
- is-this-ai-slop + Veldica + clean-writing, then Vale JSON merged onto the same
31
- report.
35
+ is-this-ai-slop + Veldica + clean-writing + ai-slop-linter + slop-detector +
36
+ prose-slop, then Vale JSON merged onto the same report.
32
37
 
33
38
  Licenses for this package and the vendored Vale styles are in [LICENSE](./LICENSE)
34
39
  and [NOTICE](./NOTICE).
package/dist/cli.js CHANGED
@@ -567,6 +567,9 @@ var ENGINE_PRESETS = {
567
567
  isThisAiSlop: true,
568
568
  veldica: true,
569
569
  cleanWriting: true,
570
+ aiSlopLinter: true,
571
+ slopDetector: true,
572
+ proseSlop: true,
570
573
  vale: true
571
574
  },
572
575
  strict: {
@@ -577,6 +580,9 @@ var ENGINE_PRESETS = {
577
580
  isThisAiSlop: true,
578
581
  veldica: true,
579
582
  cleanWriting: true,
583
+ aiSlopLinter: true,
584
+ slopDetector: true,
585
+ proseSlop: true,
580
586
  vale: true
581
587
  },
582
588
  ai: {
@@ -587,6 +593,9 @@ var ENGINE_PRESETS = {
587
593
  isThisAiSlop: true,
588
594
  veldica: true,
589
595
  cleanWriting: true,
596
+ aiSlopLinter: true,
597
+ slopDetector: true,
598
+ proseSlop: true,
590
599
  vale: true
591
600
  },
592
601
  grammar: {
@@ -597,6 +606,9 @@ var ENGINE_PRESETS = {
597
606
  isThisAiSlop: false,
598
607
  veldica: false,
599
608
  cleanWriting: true,
609
+ aiSlopLinter: false,
610
+ slopDetector: false,
611
+ proseSlop: false,
600
612
  vale: true
601
613
  }
602
614
  };
@@ -617,6 +629,9 @@ var ENGINE_KEYS = [
617
629
  "isThisAiSlop",
618
630
  "veldica",
619
631
  "cleanWriting",
632
+ "aiSlopLinter",
633
+ "slopDetector",
634
+ "proseSlop",
620
635
  "vale"
621
636
  ];
622
637
  function isObject(value) {
@@ -941,6 +956,30 @@ function isStrictPreset(config) {
941
956
  // src/node/check-files.ts
942
957
  import fs3 from "fs";
943
958
 
959
+ // src/engines/ai-slop-linter.ts
960
+ import { lintText } from "ai-slop-linter";
961
+ function toSeverity(value) {
962
+ if (value === "error") return "error";
963
+ if (value === "warning" || value === "warn") return "warn";
964
+ return "info";
965
+ }
966
+ function runAiSlopLinter(text, options = {}) {
967
+ const result = lintText(options.source ?? "<text>", text, { floor: 0 });
968
+ return result.findings.map((finding) => {
969
+ const issue = {
970
+ rule: `asl-${finding.rule}`,
971
+ engine: "ai-slop-linter",
972
+ category: "ai",
973
+ severity: toSeverity(finding.severity),
974
+ message: finding.message,
975
+ line: finding.line,
976
+ column: finding.column,
977
+ excerpt: finding.excerpt
978
+ };
979
+ return issue;
980
+ });
981
+ }
982
+
944
983
  // node_modules/clean-writing-lint/src/mask.js
945
984
  function blank(text) {
946
985
  return text.replace(/[^\n]/g, " ");
@@ -1957,7 +1996,7 @@ var AI_RULES = /* @__PURE__ */ new Set([
1957
1996
  "banned-construction",
1958
1997
  "em-dash"
1959
1998
  ]);
1960
- function toSeverity(value) {
1999
+ function toSeverity2(value) {
1961
2000
  if (value === "error") return "error";
1962
2001
  if (value === "warn") return "warn";
1963
2002
  return "info";
@@ -1977,7 +2016,7 @@ function runCleanWriting(text) {
1977
2016
  rule: `cws-${violation.rule}`,
1978
2017
  engine: "clean-writing",
1979
2018
  category: AI_RULES.has(violation.rule) ? "ai" : "grammar",
1980
- severity: toSeverity(violation.severity),
2019
+ severity: toSeverity2(violation.severity),
1981
2020
  message: violation.message,
1982
2021
  line: violation.line,
1983
2022
  column: violation.column,
@@ -2008,6 +2047,36 @@ function runIsThisAiSlop(text) {
2008
2047
  });
2009
2048
  }
2010
2049
 
2050
+ // src/engines/prose-slop.ts
2051
+ import { createRequire } from "module";
2052
+ var require2 = createRequire(import.meta.url);
2053
+ var { check } = require2("prose-slop");
2054
+ function toSeverity3(value) {
2055
+ if (value === "error") return "error";
2056
+ if (value === "warning" || value === "warn") return "warn";
2057
+ return "info";
2058
+ }
2059
+ function runProseSlop(text, options = {}) {
2060
+ const findings = check(text, {
2061
+ format: options.format ?? "plain",
2062
+ lang: "en"
2063
+ });
2064
+ return findings.map((finding) => {
2065
+ const issue = {
2066
+ rule: `prose-${finding.ruleId}`,
2067
+ engine: "prose-slop",
2068
+ category: "ai",
2069
+ severity: toSeverity3(finding.severity),
2070
+ message: finding.message,
2071
+ line: finding.line,
2072
+ column: finding.column,
2073
+ excerpt: excerptAround(text, finding.index),
2074
+ match: finding.match
2075
+ };
2076
+ return issue;
2077
+ });
2078
+ }
2079
+
2011
2080
  // node_modules/nlcst-to-string/lib/index.js
2012
2081
  var emptyNodes = [];
2013
2082
  function toString(value) {
@@ -2968,10 +3037,10 @@ function anyFactory(tests) {
2968
3037
  return false;
2969
3038
  }
2970
3039
  }
2971
- function propertiesFactory(check) {
3040
+ function propertiesFactory(check2) {
2972
3041
  const checkAsRecord = (
2973
3042
  /** @type {Record<string, unknown>} */
2974
- check
3043
+ check2
2975
3044
  );
2976
3045
  return castFactory(all);
2977
3046
  function all(node) {
@@ -2981,21 +3050,21 @@ function propertiesFactory(check) {
2981
3050
  node
2982
3051
  );
2983
3052
  let key;
2984
- for (key in check) {
3053
+ for (key in check2) {
2985
3054
  if (nodeAsRecord[key] !== checkAsRecord[key]) return false;
2986
3055
  }
2987
3056
  return true;
2988
3057
  }
2989
3058
  }
2990
- function typeFactory(check) {
3059
+ function typeFactory(check2) {
2991
3060
  return castFactory(type);
2992
3061
  function type(node) {
2993
- return node && node.type === check;
3062
+ return node && node.type === check2;
2994
3063
  }
2995
3064
  }
2996
3065
  function castFactory(testFunction) {
2997
- return check;
2998
- function check(value, index2, parent) {
3066
+ return check2;
3067
+ function check2(value, index2, parent) {
2999
3068
  return Boolean(
3000
3069
  looksLikeANode(value) && testFunction.call(
3001
3070
  this,
@@ -3024,14 +3093,14 @@ var CONTINUE = true;
3024
3093
  var EXIT = false;
3025
3094
  var SKIP = "skip";
3026
3095
  function visitParents(tree, test, visitor, reverse) {
3027
- let check;
3096
+ let check2;
3028
3097
  if (typeof test === "function" && typeof visitor !== "function") {
3029
3098
  reverse = visitor;
3030
3099
  visitor = test;
3031
3100
  } else {
3032
- check = test;
3101
+ check2 = test;
3033
3102
  }
3034
- const is2 = convert(check);
3103
+ const is2 = convert(check2);
3035
3104
  const step = reverse ? -1 : 1;
3036
3105
  factory(tree, void 0, [])();
3037
3106
  function factory(node, index2, parents) {
@@ -9676,9 +9745,9 @@ var VFile = class {
9676
9745
  * @returns {undefined}
9677
9746
  * Nothing.
9678
9747
  */
9679
- set dirname(dirname2) {
9748
+ set dirname(dirname3) {
9680
9749
  assertPath(this.basename, "dirname");
9681
- this.path = default2.join(dirname2 || "", this.basename);
9750
+ this.path = default2.join(dirname3 || "", this.basename);
9682
9751
  }
9683
9752
  /**
9684
9753
  * Get the extname (including dot) (example: `'.js'`).
@@ -10712,6 +10781,165 @@ function runRetext(text) {
10712
10781
  });
10713
10782
  }
10714
10783
 
10784
+ // src/engines/slop-detector.ts
10785
+ import { readFileSync as readFileSync3 } from "fs";
10786
+ import { createRequire as createRequire2 } from "module";
10787
+ import { dirname as dirname2, join as join3 } from "path";
10788
+ var WEIGHT_WORD = 0.5;
10789
+ var WEIGHT_PATTERN = 0.2;
10790
+ var WEIGHT_TRIGRAM = 0.12;
10791
+ var WEIGHT_TROPE = 0.18;
10792
+ var WORD_MULTIPLIER = 1e3;
10793
+ var TRIGRAM_MULTIPLIER = 1e3;
10794
+ var CONTRAST_PATTERN = /\b(?:it(?:'s| is)|this is|that(?:'s| is)|(?:it|this|that) isn'?t)\s+(?:just |only |merely |simply )?[a-z][\s\S]{0,80}?\b(?:it(?:'s| is)|this is|that(?:'s| is)|but)\b/gi;
10795
+ var NOT_BUT_PATTERN = /\bnot\b[\s\S]{0,60}?\bbut\b/gi;
10796
+ var require3 = createRequire2(import.meta.url);
10797
+ var assetsDir = join3(
10798
+ dirname2(require3.resolve("slop-detector/package.json")),
10799
+ "dist",
10800
+ "assets"
10801
+ );
10802
+ var cachedAssets = null;
10803
+ function computeRange(values) {
10804
+ if (values.length === 0) return { min: 0, max: 1 };
10805
+ const min = Math.min(...values);
10806
+ const max = Math.max(...values);
10807
+ const range = max - min || 1;
10808
+ return { min: min - range * 0.1, max: max + range * 0.1 };
10809
+ }
10810
+ function loadAssets() {
10811
+ if (cachedAssets) return cachedAssets;
10812
+ const wordsRaw = JSON.parse(
10813
+ readFileSync3(join3(assetsDir, "slop_list.json"), "utf8")
10814
+ );
10815
+ const trigramsRaw = JSON.parse(
10816
+ readFileSync3(join3(assetsDir, "slop_list_trigrams.json"), "utf8")
10817
+ );
10818
+ const leaderboard = JSON.parse(
10819
+ readFileSync3(join3(assetsDir, "leaderboard_results.json"), "utf8")
10820
+ );
10821
+ const words = /* @__PURE__ */ new Set();
10822
+ for (const item of wordsRaw) {
10823
+ if (Array.isArray(item) && item[0]) words.add(item[0].toLowerCase());
10824
+ }
10825
+ const trigrams = /* @__PURE__ */ new Set();
10826
+ for (const item of trigramsRaw) {
10827
+ if (Array.isArray(item) && item[0]) trigrams.add(item[0].toLowerCase());
10828
+ }
10829
+ const metrics = {
10830
+ slop_words: [],
10831
+ slop_trigrams: [],
10832
+ contrast: []
10833
+ };
10834
+ for (const res of leaderboard.results ?? []) {
10835
+ if (!res.metrics) continue;
10836
+ metrics.slop_words.push(res.metrics.slop_list_matches_per_1k_words || 0);
10837
+ metrics.slop_trigrams.push(
10838
+ res.metrics.slop_trigram_matches_per_1k_words || 0
10839
+ );
10840
+ metrics.contrast.push(res.metrics.not_x_but_y_per_1k_chars || 0);
10841
+ }
10842
+ cachedAssets = {
10843
+ words,
10844
+ trigrams,
10845
+ normalizationRanges: {
10846
+ slop_words: computeRange(metrics.slop_words),
10847
+ slop_trigrams: computeRange(metrics.slop_trigrams),
10848
+ contrast: computeRange(metrics.contrast)
10849
+ }
10850
+ };
10851
+ return cachedAssets;
10852
+ }
10853
+ function normalizeValue(value, range) {
10854
+ const normalized = (value - range.min) / (range.max - range.min);
10855
+ return Math.max(0, Math.min(1, normalized));
10856
+ }
10857
+ function toPlainText(text) {
10858
+ return text.replace(/```[\s\S]*?```/g, " ").replace(/`[^`\n]+`/g, " ").replace(/<[^>]+>/g, " ").replace(/!\[[^\]]*]\([^)]*\)/g, " ").replace(/\[[^\]]*]\([^)]*\)/g, " ").replace(/^#{1,6}\s+/gm, "").replace(/[*_~>#-]+/g, " ");
10859
+ }
10860
+ function tokenize(text) {
10861
+ return text.toLowerCase().match(/[a-z]+(?:'[a-z]+)?/g) ?? [];
10862
+ }
10863
+ function collectMatches(pattern, text) {
10864
+ return [...text.matchAll(pattern)].map((match) => match[0] ?? "");
10865
+ }
10866
+ function runSlopDetector(text) {
10867
+ if (!text.trim()) return [];
10868
+ const assets = loadAssets();
10869
+ const plain = toPlainText(text);
10870
+ const tokens = tokenize(plain);
10871
+ const nTokens = tokens.length || 1;
10872
+ let wordHitCount = 0;
10873
+ const wordHitMap = /* @__PURE__ */ new Map();
10874
+ for (const token of tokens) {
10875
+ if (!assets.words.has(token)) continue;
10876
+ wordHitCount++;
10877
+ wordHitMap.set(token, (wordHitMap.get(token) || 0) + 1);
10878
+ }
10879
+ let trigramHitCount = 0;
10880
+ if (tokens.length >= 3) {
10881
+ for (let i = 0; i < tokens.length - 2; i++) {
10882
+ const tg = `${tokens[i]} ${tokens[i + 1]} ${tokens[i + 2]}`;
10883
+ if (assets.trigrams.has(tg)) trigramHitCount++;
10884
+ }
10885
+ }
10886
+ const contrastMatches = [
10887
+ .../* @__PURE__ */ new Set([
10888
+ ...collectMatches(CONTRAST_PATTERN, plain),
10889
+ ...collectMatches(NOT_BUT_PATTERN, plain)
10890
+ ])
10891
+ ];
10892
+ const emDashCount = (text.match(/—/g) ?? []).length;
10893
+ const tropeRate = emDashCount / Math.max(plain.length, 1) * 1e3;
10894
+ const wordScore = wordHitCount / nTokens * WORD_MULTIPLIER;
10895
+ const trigramScore = trigramHitCount / nTokens * TRIGRAM_MULTIPLIER;
10896
+ const contrastScore = contrastMatches.length / Math.max(plain.length, 1) * 1e3;
10897
+ const rawSlopScore = (normalizeValue(wordScore, assets.normalizationRanges.slop_words) * WEIGHT_WORD + normalizeValue(contrastScore, assets.normalizationRanges.contrast) * WEIGHT_PATTERN + normalizeValue(trigramScore, assets.normalizationRanges.slop_trigrams) * WEIGHT_TRIGRAM + normalizeValue(tropeRate, { min: 0, max: 5 }) * WEIGHT_TROPE) * 100;
10898
+ const slopScore = Math.round(rawSlopScore * 10) / 10;
10899
+ const issues = [];
10900
+ if (slopScore >= 40) {
10901
+ issues.push({
10902
+ rule: "eqbench-slop-score",
10903
+ engine: "slop-detector",
10904
+ category: "ai",
10905
+ severity: slopScore >= 60 ? "warn" : "info",
10906
+ message: `EQBench SLOP score ${slopScore}/100 (higher = more AI-like).`,
10907
+ suggestion: slopScore >= 60 ? "Rewrite stock phrasing and contrast constructions." : "Review flagged vocabulary and contrast patterns."
10908
+ });
10909
+ }
10910
+ for (const [word2, count] of [...wordHitMap.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8)) {
10911
+ issues.push({
10912
+ rule: `eqbench-word-${word2.replace(/\s+/g, "-")}`,
10913
+ engine: "slop-detector",
10914
+ category: "ai",
10915
+ severity: "info",
10916
+ message: `EQBench slop vocabulary: \u201C${word2}\u201D (${count}\xD7).`,
10917
+ match: word2
10918
+ });
10919
+ }
10920
+ for (const match of contrastMatches.slice(0, 5)) {
10921
+ issues.push({
10922
+ rule: "eqbench-contrast",
10923
+ engine: "slop-detector",
10924
+ category: "ai",
10925
+ severity: "info",
10926
+ message: `EQBench contrast pattern: \u201C${match.trim()}\u201D`,
10927
+ match
10928
+ });
10929
+ }
10930
+ if (emDashCount >= 3) {
10931
+ issues.push({
10932
+ rule: "eqbench-trope-em-dash",
10933
+ engine: "slop-detector",
10934
+ category: "ai",
10935
+ severity: "info",
10936
+ message: `EQBench typography trope: ${emDashCount} em dash(es).`,
10937
+ match: "\u2014"
10938
+ });
10939
+ }
10940
+ return issues;
10941
+ }
10942
+
10715
10943
  // src/engines/tag.ts
10716
10944
  function tagEngine(issues, engine) {
10717
10945
  return issues.map((issue) => ({ ...issue, engine }));
@@ -11082,7 +11310,7 @@ function countListItems(input) {
11082
11310
  function countBlockquotes(input) {
11083
11311
  return getLineMetadata(input).blockquote_count;
11084
11312
  }
11085
- function tokenize(text) {
11313
+ function tokenize2(text) {
11086
11314
  const blocks = splitParagraphBlocks(text);
11087
11315
  const paragraphs2 = blocks.map((block) => block.text);
11088
11316
  const sentences2 = splitSentencesFromBlocks(blocks);
@@ -11104,7 +11332,7 @@ function tokenize(text) {
11104
11332
  }
11105
11333
  };
11106
11334
  }
11107
- var tokenizeProse = tokenize;
11335
+ var tokenizeProse = tokenize2;
11108
11336
 
11109
11337
  // node_modules/@veldica/prose-analyzer/dist/utils.js
11110
11338
  function round(value, decimals = 4) {
@@ -11848,7 +12076,7 @@ function inventoryMarkers(text, options = {}) {
11848
12076
  }
11849
12077
 
11850
12078
  // src/engines/veldica.ts
11851
- function toSeverity2(value) {
12079
+ function toSeverity4(value) {
11852
12080
  if (value === "high") return "warn";
11853
12081
  return "info";
11854
12082
  }
@@ -11863,7 +12091,7 @@ function runVeldica(text) {
11863
12091
  rule: `veldica-${match.category || "marker"}`,
11864
12092
  engine: "veldica",
11865
12093
  category: "ai",
11866
- severity: toSeverity2(match.severity),
12094
+ severity: toSeverity4(match.severity),
11867
12095
  message: `AI-style marker (${match.category}): \u201C${match.matched_text}\u201D`,
11868
12096
  excerpt: excerptAround(text, offset),
11869
12097
  match: match.matched_text
@@ -11878,9 +12106,9 @@ function runVeldica(text) {
11878
12106
  }
11879
12107
 
11880
12108
  // src/engines/write-good.ts
11881
- import { createRequire } from "module";
11882
- var require2 = createRequire(import.meta.url);
11883
- var writeGood = require2("write-good");
12109
+ import { createRequire as createRequire3 } from "module";
12110
+ var require4 = createRequire3(import.meta.url);
12111
+ var writeGood = require4("write-good");
11884
12112
  function runWriteGood(text) {
11885
12113
  const suggestions = writeGood(text, {
11886
12114
  so: false,
@@ -12976,7 +13204,7 @@ function computeStats(body, titleLength, descriptionLength, issues) {
12976
13204
  descriptionLength
12977
13205
  };
12978
13206
  }
12979
- function collectIssues(body, frontmatter, format, config) {
13207
+ function collectIssues(body, frontmatter, format, config, source2) {
12980
13208
  const ignoreRules = new Set(config.ignoreRules);
12981
13209
  const issues = [];
12982
13210
  const enginesRun = [];
@@ -13049,6 +13277,18 @@ function collectIssues(body, frontmatter, format, config) {
13049
13277
  enginesRun.push("clean-writing");
13050
13278
  issues.push(...runCleanWriting(body));
13051
13279
  }
13280
+ if (config.engines.aiSlopLinter && config.checks.ai) {
13281
+ enginesRun.push("ai-slop-linter");
13282
+ issues.push(...runAiSlopLinter(body, { source: source2 }));
13283
+ }
13284
+ if (config.engines.slopDetector && config.checks.ai) {
13285
+ enginesRun.push("slop-detector");
13286
+ issues.push(...runSlopDetector(body));
13287
+ }
13288
+ if (config.engines.proseSlop && config.checks.ai) {
13289
+ enginesRun.push("prose-slop");
13290
+ issues.push(...runProseSlop(body, { format }));
13291
+ }
13052
13292
  return {
13053
13293
  issues: issues.filter((issue) => !ignoreRules.has(issue.rule)),
13054
13294
  enginesRun: uniqueEngines(enginesRun)
@@ -13064,7 +13304,8 @@ function checkText(text, options = {}) {
13064
13304
  body,
13065
13305
  parsed.frontmatter,
13066
13306
  format,
13067
- config
13307
+ config,
13308
+ source2
13068
13309
  );
13069
13310
  const title = String(parsed.frontmatter.title ?? pathBasename(source2));
13070
13311
  const report = {
@@ -13097,11 +13338,11 @@ import path2 from "path";
13097
13338
 
13098
13339
  // src/node/vale.ts
13099
13340
  import { spawnSync } from "child_process";
13100
- import { createRequire as createRequire2 } from "module";
13341
+ import { createRequire as createRequire4 } from "module";
13101
13342
  import fs from "fs";
13102
13343
  import path from "path";
13103
13344
  import { fileURLToPath as fileURLToPath2 } from "url";
13104
- var require3 = createRequire2(import.meta.url);
13345
+ var require5 = createRequire4(import.meta.url);
13105
13346
  function packageRoot() {
13106
13347
  let current = path.dirname(fileURLToPath2(import.meta.url));
13107
13348
  while (true) {
@@ -13140,7 +13381,7 @@ function findValeBinary(cwd = process.cwd()) {
13140
13381
  const fromPackage = firstExisting(valeNativeCandidates(packageRoot()));
13141
13382
  if (fromPackage) return fromPackage;
13142
13383
  try {
13143
- const pkg = require3.resolve("@vvago/vale/package.json");
13384
+ const pkg = require5.resolve("@vvago/vale/package.json");
13144
13385
  const dir = path.dirname(pkg);
13145
13386
  const resolved = firstExisting([
13146
13387
  path.join(
@@ -13197,8 +13438,8 @@ function valeSeverity(value) {
13197
13438
  if (value === "suggestion") return "info";
13198
13439
  return "warn";
13199
13440
  }
13200
- function valeCategory(check) {
13201
- if (check.startsWith("write-good.") || check.startsWith("Google.")) {
13441
+ function valeCategory(check2) {
13442
+ if (check2.startsWith("write-good.") || check2.startsWith("Google.")) {
13202
13443
  return "grammar";
13203
13444
  }
13204
13445
  return "ai";
@@ -13224,13 +13465,13 @@ function collectValeIssues(files, options = {}) {
13224
13465
  const issues = [];
13225
13466
  for (const raw of alerts) {
13226
13467
  const alert = raw;
13227
- const check = alert.Check ?? "vale";
13468
+ const check2 = alert.Check ?? "vale";
13228
13469
  const issue = {
13229
- rule: check,
13470
+ rule: check2,
13230
13471
  engine: "vale",
13231
- category: valeCategory(check),
13472
+ category: valeCategory(check2),
13232
13473
  severity: valeSeverity(alert.Severity),
13233
- message: alert.Message ?? check
13474
+ message: alert.Message ?? check2
13234
13475
  };
13235
13476
  if (alert.Line) issue.line = alert.Line;
13236
13477
  if (alert.Span?.[0]) issue.column = alert.Span[0];
@@ -13467,8 +13708,9 @@ Options:
13467
13708
  -- Extra flags after -- are passed to Vale
13468
13709
 
13469
13710
  By default every local engine runs: polish, slop-gate, write-good, retext,
13470
- is-this-ai-slop, Veldica, clean-writing, and Vale. Emoji in prose is flagged
13471
- as an AI tell unless --ignore-emojis (or ignoreEmojis: true) is set.
13711
+ is-this-ai-slop, Veldica, clean-writing, ai-slop-linter, slop-detector,
13712
+ prose-slop, and Vale. Emoji in prose is flagged as an AI tell unless
13713
+ --ignore-emojis (or ignoreEmojis: true) is set.
13472
13714
 
13473
13715
  Examples:
13474
13716
  ai-text-check content
@@ -13634,9 +13876,9 @@ function toUserConfig(options) {
13634
13876
  // src/cli/init.ts
13635
13877
  import fs5 from "fs";
13636
13878
  import path5 from "path";
13637
- import { createRequire as createRequire3 } from "module";
13879
+ import { createRequire as createRequire5 } from "module";
13638
13880
  import { fileURLToPath as fileURLToPath3 } from "url";
13639
- var require4 = createRequire3(import.meta.url);
13881
+ var require6 = createRequire5(import.meta.url);
13640
13882
  function packageVersion() {
13641
13883
  let current = path5.dirname(fileURLToPath3(import.meta.url));
13642
13884
  while (true) {
@@ -13649,7 +13891,7 @@ function packageVersion() {
13649
13891
  if (parent === current) break;
13650
13892
  current = parent;
13651
13893
  }
13652
- return require4("../../package.json").version;
13894
+ return require6("../../package.json").version;
13653
13895
  }
13654
13896
  function findPackageRoot() {
13655
13897
  let current = path5.dirname(fileURLToPath3(import.meta.url));