slopless 0.2.25 → 0.2.27

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
@@ -6,7 +6,7 @@
6
6
  [![license](https://img.shields.io/npm/l/slopless)](LICENSE)
7
7
  [![node](https://img.shields.io/badge/node-22%2B-brightgreen)](package.json)
8
8
 
9
- [![ci](https://img.shields.io/github/actions/workflow/status/seochecks-ai%2Fslopless/ci.yml?branch=main&label=ci)](/actions/workflows/ci.yml)
9
+ [![ci](https://img.shields.io/github/actions/workflow/status/berelevant-ai%2Fslopless/ci.yml?branch=main&label=ci)](/actions/workflows/ci.yml)
10
10
  [![socket](https://socket.dev/api/badge/npm/package/slopless)](https://socket.dev/npm/package/slopless)
11
11
 
12
12
  Give it to your writing agent and it stops handing you AI-slop prose. Slopless is a deterministic linter - a textlint preset and a zero-config CLI - that flags the LLM tells (hollow framing, fake contrasts, hedging, em-dash tics, vacuous closers, and many more) so the agent rewrites until the text reads human. No model calls, no API key.
@@ -33,7 +33,7 @@ to the teams that slow down to think.
33
33
  [slopless/prohibited-phrases] the future belongs to
34
34
  ```
35
35
 
36
- Full JSON output is the default. See the [Rules](https://github.com/seochecks-ai/slopless/wiki/Rules) page for the complete inventory.
36
+ Full JSON output is the default. See the [Rules](https://github.com/berelevant-ai/slopless/wiki/Rules) page for the complete inventory.
37
37
 
38
38
  ## Intended Usage Loop
39
39
 
@@ -141,12 +141,12 @@ Something shifted in the room.
141
141
 
142
142
  ## More
143
143
 
144
- - [Philosophy](https://github.com/seochecks-ai/slopless/wiki/Philosophy) - what slopless is for, design principles, why deterministic.
145
- - [Comparison](https://github.com/seochecks-ai/slopless/wiki/Comparison) - slopless vs proselint, write-good, alex, vale, default textlint presets.
146
- - [Rules](https://github.com/seochecks-ai/slopless/wiki/Rules) - full 50+ rule inventory across seven families.
147
- - [Behavior](https://github.com/seochecks-ai/slopless/wiki/Behavior) - CLI flags, exit codes, JSON output shape, direct textlint integration.
148
- - [Ignore rules](https://github.com/seochecks-ai/slopless/wiki/Ignore-Rules) - inline `textlint-disable` block syntax.
149
- - [Thanks](https://github.com/seochecks-ai/slopless/wiki/Thanks) - direct rule sources, dependencies, and acknowledgments.
144
+ - [Philosophy](https://github.com/berelevant-ai/slopless/wiki/Philosophy) - what slopless is for, design principles, why deterministic.
145
+ - [Comparison](https://github.com/berelevant-ai/slopless/wiki/Comparison) - slopless vs proselint, write-good, alex, vale, default textlint presets.
146
+ - [Rules](https://github.com/berelevant-ai/slopless/wiki/Rules) - full 50+ rule inventory across seven families.
147
+ - [Behavior](https://github.com/berelevant-ai/slopless/wiki/Behavior) - CLI flags, exit codes, JSON output shape, direct textlint integration.
148
+ - [Ignore rules](https://github.com/berelevant-ai/slopless/wiki/Ignore-Rules) - inline `textlint-disable` block syntax.
149
+ - [Thanks](https://github.com/berelevant-ai/slopless/wiki/Thanks) - direct rule sources, dependencies, and acknowledgments.
150
150
  - [Roadmap](ROADMAP.md) - near-term direction and links to active plans.
151
151
  - [Contributing](.github/CONTRIBUTING.md) - open a detailed issue first; PRs must pass the G3TS pre-commit gate.
152
152
 
@@ -166,4 +166,4 @@ This confirms the tarball you installed was built in the published source reposi
166
166
 
167
167
  ---
168
168
 
169
- Developed by [seochecks.ai](https://seochecks.ai) to keep content specific, useful, and recognizably human.
169
+ Developed by [berelevant.ai](https://berelevant.ai) to keep content specific, useful, and recognizably human.
@@ -159,7 +159,7 @@ function matchWhatFrame(words) {
159
159
  function matchAbstractFrame(text) {
160
160
  const words = tokens(text);
161
161
  return (matchRelativeDiscourseFrame(text, words) ??
162
- matchExpandedDiscourseFrame(words) ??
162
+ matchExpandedDiscourseFrame(text, words) ??
163
163
  matchModifiedAbstractFrame(words) ??
164
164
  matchDiscourseEvaluationFrame(words) ??
165
165
  matchPointIsToFrame(words) ??
@@ -221,6 +221,7 @@ function matchSignposting(sentence) {
221
221
  const generatedFormula = matchGeneratedFormula(stripped);
222
222
  if (abstract !== undefined &&
223
223
  (abstract === "what-matters-is" ||
224
+ abstract.startsWith("deictic-evaluative-") ||
224
225
  abstract.startsWith("relative-") ||
225
226
  !concreteImplementation)) {
226
227
  return { kind: "abstract-evaluation-frame", signal: abstract };
@@ -5,6 +5,53 @@ const DISCOURSE_WORK_TAILS = [
5
5
  ["load", "bearing"]
6
6
  ];
7
7
  const DETERMINERS = new Set(["a", "an", "the", "this", "that"]);
8
+ const DEICTIC_OPENERS = new Set(["here", "this", "that"]);
9
+ const DEICTIC_CONTRACTIONS = new Map([
10
+ ["here's", "here"],
11
+ ["this's", "this"],
12
+ ["that's", "that"]
13
+ ]);
14
+ const DEICTIC_EVALUATIVE_ADJECTIVES = new Set([
15
+ "best",
16
+ "better",
17
+ "biggest",
18
+ "central",
19
+ "core",
20
+ "crucial",
21
+ "funny",
22
+ "good",
23
+ "great",
24
+ "hard",
25
+ "important",
26
+ "interesting",
27
+ "key",
28
+ "main",
29
+ "neat",
30
+ "nice",
31
+ "odd",
32
+ "obvious",
33
+ "remarkable",
34
+ "strange",
35
+ "surprising",
36
+ "tricky",
37
+ "useful",
38
+ "weird",
39
+ "wild"
40
+ ]);
41
+ const DEICTIC_DISCOURSE_NOUNS = new Set([
42
+ "angle",
43
+ "aspect",
44
+ "bit",
45
+ "catch",
46
+ "detail",
47
+ "element",
48
+ "idea",
49
+ "part",
50
+ "piece",
51
+ "point",
52
+ "thing",
53
+ "twist"
54
+ ]);
8
55
  const FRAME_ADJECTIVES = new Set([
9
56
  "basic",
10
57
  "best",
@@ -202,13 +249,45 @@ function matchEvaluativeFrame(words) {
202
249
  ? `the-${adjective}-${noun}-${verb}`
203
250
  : undefined;
204
251
  }
252
+ function matchDeicticEvaluativeFrame(text, words) {
253
+ const contractedOpener = DEICTIC_CONTRACTIONS.get(words[0] ?? "");
254
+ const opener = contractedOpener ?? words[0];
255
+ const determinerIndex = contractedOpener === undefined ? 2 : 1;
256
+ if (opener === undefined ||
257
+ !DEICTIC_OPENERS.has(opener) ||
258
+ (contractedOpener === undefined &&
259
+ !["is", "was"].includes(words[1] ?? "")) ||
260
+ !["a", "an", "the"].includes(words[determinerIndex] ?? "")) {
261
+ return undefined;
262
+ }
263
+ const adjective = words[determinerIndex + 1];
264
+ const noun = words[determinerIndex + 2];
265
+ const frame = adjective !== undefined && noun !== undefined
266
+ ? words.slice(0, determinerIndex + 3).join(" ")
267
+ : undefined;
268
+ const boundary = frame === undefined ? undefined : text.at(frame.length);
269
+ return adjective !== undefined &&
270
+ noun !== undefined &&
271
+ (boundary === undefined ||
272
+ boundary === "." ||
273
+ boundary === "!" ||
274
+ boundary === "?" ||
275
+ boundary === ":" ||
276
+ boundary === ";" ||
277
+ boundary === ",") &&
278
+ DEICTIC_EVALUATIVE_ADJECTIVES.has(adjective) &&
279
+ DEICTIC_DISCOURSE_NOUNS.has(noun)
280
+ ? `deictic-evaluative-${opener}-${adjective}-${noun}`
281
+ : undefined;
282
+ }
205
283
  export function isAbstractAuditFrame(words) {
206
284
  return words[0] === "the" && frameNounIndex(words) > 0
207
285
  ? words[frameNounIndex(words)] === "audit"
208
286
  : false;
209
287
  }
210
- export function matchExpandedDiscourseFrame(words) {
211
- return (matchWorthAttentionFrame(words) ??
288
+ export function matchExpandedDiscourseFrame(text, words) {
289
+ return (matchDeicticEvaluativeFrame(text, words) ??
290
+ matchWorthAttentionFrame(words) ??
212
291
  matchVagueFrameLocation(words) ??
213
292
  matchEvaluativeFrame(words));
214
293
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopless",
3
- "version": "0.2.25",
3
+ "version": "0.2.27",
4
4
  "description": "Deterministic textlint rules and CLI for catching prose slop in English Markdown.",
5
5
  "keywords": [
6
6
  "textlint",
@@ -19,18 +19,18 @@
19
19
  "cli"
20
20
  ],
21
21
  "license": "MIT",
22
- "author": "seochecks.ai (https://seochecks.ai)",
22
+ "author": "berelevant.ai (https://berelevant.ai)",
23
23
  "publishConfig": {
24
24
  "access": "public",
25
25
  "provenance": true
26
26
  },
27
27
  "repository": {
28
28
  "type": "git",
29
- "url": "git+https://github.com/seochecks-ai/slopless.git"
29
+ "url": "git+https://github.com/berelevant-ai/slopless.git"
30
30
  },
31
- "homepage": "https://github.com/seochecks-ai/slopless#readme",
31
+ "homepage": "https://github.com/berelevant-ai/slopless#readme",
32
32
  "bugs": {
33
- "url": "https://github.com/seochecks-ai/slopless/issues"
33
+ "url": "https://github.com/berelevant-ai/slopless/issues"
34
34
  },
35
35
  "type": "module",
36
36
  "main": "./dist/index.js",