slopless 0.2.15 → 0.2.16

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.
@@ -1,5 +1,16 @@
1
- import { findVocabularyMatches } from "./private/vocabulary-context.js";
2
- import { oneToOneRule } from "../private/textlint-rule-builders.js";
1
+ import { defineTextlintRule } from "../../adapters/textlint/rule.js";
2
+ import { paragraphUnits } from "../../adapters/textlint/units.js";
3
+ import { wordTokens } from "../../shared/text/tokens.js";
4
+ import { isVocabularyContextAllowed } from "./private/vocabulary-context.js";
5
+ const RULE_ID = "words:llm-vocabulary";
6
+ const GROUP = "llm vocabulary";
7
+ const MAX_PARAGRAPH_TOKENS = 90;
8
+ const MAX_WINDOW_TOKENS = 65;
9
+ const MIN_HITS = 3;
10
+ const WINDOW_SENTENCES = 4;
11
+ // Single stock words like "comprehensive" or "moreover" are ordinary in human
12
+ // prose; flag only when stock LLM diction clusters. (Words that are slop on
13
+ // their own, e.g. "delve", are still caught per-instance by prohibited-words.)
3
14
  const LLM_VOCABULARY = new Set([
4
15
  "delve",
5
16
  "vibrant",
@@ -14,16 +25,39 @@ const LLM_VOCABULARY = new Set([
14
25
  "moreover",
15
26
  "tapestry"
16
27
  ]);
17
- const rule = oneToOneRule({
18
- detect: (unit) => findVocabularyMatches(unit.text, [...LLM_VOCABULARY]).map((match) => ({
19
- evidence: match.text,
20
- label: match.text,
21
- range: { start: match.start, end: match.end }
22
- })),
23
- family: "words",
24
- formatMessage: (report) => `LLM vocabulary found: "${report.evidence}". Replace the stock diction with a concrete word.`,
25
- ruleId: "words:llm-vocabulary",
26
- unitKind: "str"
28
+ function vocabularyDetections(unit) {
29
+ return wordTokens(unit.text)
30
+ .filter((token) => LLM_VOCABULARY.has(token.normalized) &&
31
+ !isVocabularyContextAllowed(unit.text, token.normalized))
32
+ .map((token) => ({
33
+ evidence: unit.text.slice(token.start, token.end),
34
+ group: GROUP,
35
+ label: token.normalized,
36
+ range: { end: token.end, start: token.start },
37
+ ruleId: RULE_ID,
38
+ unitId: unit.id
39
+ }));
40
+ }
41
+ const rule = defineTextlintRule({
42
+ detector: {
43
+ detect: ({ units }) => units.flatMap((unit) => vocabularyDetections(unit)),
44
+ family: "words",
45
+ id: RULE_ID
46
+ },
47
+ formatMessage: (report) => {
48
+ const labels = [...new Set(report.detections.map((hit) => hit.label))];
49
+ return `LLM vocabulary density: ${report.detections.length} stock words in a short span (${labels.join(", ")}). Replace the stock diction with concrete words.`;
50
+ },
51
+ reportPolicy: {
52
+ groups: [GROUP],
53
+ kind: "density",
54
+ maxParagraphTokens: MAX_PARAGRAPH_TOKENS,
55
+ maxWindowTokens: MAX_WINDOW_TOKENS,
56
+ paragraphMinimumHits: MIN_HITS,
57
+ windowMinimumHits: MIN_HITS,
58
+ windowSentences: WINDOW_SENTENCES
59
+ },
60
+ units: (document) => paragraphUnits(document)
27
61
  });
28
62
  export default rule;
29
63
  //# sourceMappingURL=llm-vocabulary.js.map
@@ -1,26 +1,50 @@
1
1
  import simplicityPairs from "./data/simplicity-pairs.json" with { type: "json" };
2
+ import { defineTextlintRule } from "../../adapters/textlint/rule.js";
3
+ import { paragraphUnits } from "../../adapters/textlint/units.js";
2
4
  import { wordTokens } from "../../shared/text/tokens.js";
3
- import { oneToOneRule } from "../private/textlint-rule-builders.js";
4
- const SIMPLE_BY_COMPLEX = new Map(simplicityPairs.map(([complex, simple]) => [complex, simple]));
5
- const rule = oneToOneRule({
6
- detect: (unit) => wordTokens(unit.text).flatMap((token) => {
7
- const simple = SIMPLE_BY_COMPLEX.get(token.normalized);
8
- if (simple === undefined) {
9
- return [];
10
- }
11
- return [
12
- {
13
- data: { simple },
14
- evidence: token.text,
15
- label: token.text,
16
- range: { start: token.start, end: token.end }
17
- }
18
- ];
19
- }),
20
- family: "words",
21
- formatMessage: (report) => `Complex word found: "${report.evidence}". Use "${report.detections[0]?.data?.["simple"]}" instead.`,
22
- ruleId: "words:simplicity",
23
- unitKind: "sentence"
5
+ const RULE_ID = "words:simplicity";
6
+ const GROUP = "complex word";
7
+ const MAX_PARAGRAPH_TOKENS = 90;
8
+ const MAX_WINDOW_TOKENS = 65;
9
+ const MIN_HITS = 2;
10
+ const WINDOW_SENTENCES = 4;
11
+ // A single complex word is not slop; one "numerous" or "utilize" is ordinary.
12
+ // Flag only when complex diction clusters, which is what makes prose feel dense.
13
+ const COMPLEX_WORDS = new Set(simplicityPairs
14
+ .map((pair) => pair[0])
15
+ .filter((word) => word !== undefined));
16
+ function complexDetections(unit) {
17
+ return wordTokens(unit.text)
18
+ .filter((token) => COMPLEX_WORDS.has(token.normalized))
19
+ .map((token) => ({
20
+ evidence: unit.text.slice(token.start, token.end),
21
+ group: GROUP,
22
+ label: token.normalized,
23
+ range: { end: token.end, start: token.start },
24
+ ruleId: RULE_ID,
25
+ unitId: unit.id
26
+ }));
27
+ }
28
+ const rule = defineTextlintRule({
29
+ detector: {
30
+ detect: ({ units }) => units.flatMap((unit) => complexDetections(unit)),
31
+ family: "words",
32
+ id: RULE_ID
33
+ },
34
+ formatMessage: (report) => {
35
+ const labels = [...new Set(report.detections.map((hit) => hit.label))];
36
+ return `Complex word density: ${report.detections.length} complex words in a short span (${labels.join(", ")}). Prefer simpler wording.`;
37
+ },
38
+ reportPolicy: {
39
+ groups: [GROUP],
40
+ kind: "density",
41
+ maxParagraphTokens: MAX_PARAGRAPH_TOKENS,
42
+ maxWindowTokens: MAX_WINDOW_TOKENS,
43
+ paragraphMinimumHits: MIN_HITS,
44
+ windowMinimumHits: MIN_HITS,
45
+ windowSentences: WINDOW_SENTENCES
46
+ },
47
+ units: (document) => paragraphUnits(document)
24
48
  });
25
49
  export default rule;
26
50
  //# sourceMappingURL=simplicity.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopless",
3
- "version": "0.2.15",
3
+ "version": "0.2.16",
4
4
  "description": "Deterministic textlint rules and CLI for catching prose slop in English Markdown.",
5
5
  "keywords": [
6
6
  "textlint",