slopless 0.1.0 → 0.2.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/README.md CHANGED
@@ -1,64 +1,190 @@
1
1
  # slopless
2
2
 
3
- Deterministic textlint rules for catching slop in prose.
3
+ Install:
4
4
 
5
- ## Install
5
+ ```bash
6
+ npm install -D slopless
7
+ ```
8
+
9
+ Run:
6
10
 
7
11
  ```bash
8
- npm install -D textlint slopless
12
+ npx slopless
9
13
  ```
10
14
 
11
- ## Configure
15
+ Run on a specific path:
12
16
 
13
- Create `.textlintrc.json`:
17
+ ```bash
18
+ npx slopless "docs/**/*.md"
19
+ ```
14
20
 
15
- ```json
16
- {
17
- "rules": {
18
- "preset-slopless": true
19
- }
20
- }
21
+ Save JSON:
22
+
23
+ ```bash
24
+ npx slopless "docs/**/*.md" > slopless.json
21
25
  ```
22
26
 
23
- ## Run
27
+ ## Npm Script
24
28
 
25
- Add an npm script:
29
+ Add this to `package.json`:
26
30
 
27
31
  ```json
28
32
  {
29
33
  "scripts": {
30
- "lint:prose": "textlint \"**/*.md\""
34
+ "lint:prose": "slopless"
31
35
  }
32
36
  }
33
37
  ```
34
38
 
35
- Run it:
39
+ Run:
40
+
41
+ ```bash
42
+ npm run lint:prose
43
+ ```
44
+
45
+ ## What Slopless Is
46
+
47
+ Slopless is a deterministic prose checker for Markdown.
48
+
49
+ It reports concrete writing patterns that often make generated or careless prose feel padded, vague, generic, or formulaic.
50
+
51
+ It is built for local scripts, CI checks, review pipelines, and tools that need structured prose findings without calling an LLM.
52
+
53
+ ## What Slopless Is Not
54
+
55
+ Slopless does not rewrite text.
56
+
57
+ Slopless does not check facts.
58
+
59
+ Slopless does not judge taste.
60
+
61
+ Slopless does not replace editing.
62
+
63
+ It reports rule findings. A person or another tool decides what to do with them.
64
+
65
+ ## Defaults
66
+
67
+ - Checks `**/*.md` when no path is passed.
68
+ - Emits JSON only.
69
+ - Requires Node.js 20 or newer.
70
+ - Requires no `.textlintrc.json`.
71
+ - Requires no separate `textlint` install.
72
+
73
+ ## Exit Codes
74
+
75
+ - `0`: no findings
76
+ - `1`: prose findings were reported
77
+ - `2`: command failure before linting
78
+
79
+ ## Output
80
+
81
+ Output is always textlint JSON.
82
+
83
+ Each result contains the checked file path and its messages. Each message includes the rule ID, line, column, message text, and range data when textlint can provide it.
84
+
85
+ Rule IDs use this shape:
86
+
87
+ ```text
88
+ slopless/<rule-name>
89
+ ```
90
+
91
+ Example:
92
+
93
+ ```text
94
+ slopless/semantic-thinness
95
+ slopless/llm-openers
96
+ slopless/hedge-stacking
97
+ ```
98
+
99
+ ## CI Use
100
+
101
+ Run Slopless in CI:
36
102
 
37
103
  ```bash
104
+ npm ci
38
105
  npm run lint:prose
39
106
  ```
40
107
 
41
- ## CI Output
108
+ Save findings as an artifact:
109
+
110
+ ```bash
111
+ npx slopless "docs/**/*.md" > slopless.json
112
+ ```
113
+
114
+ The command exits `1` when findings exist, so CI fails by default on reported prose issues.
115
+
116
+ ## Stdin
117
+
118
+ Check text from stdin:
119
+
120
+ ```bash
121
+ cat draft.md | npx slopless --stdin --stdin-filename draft.md
122
+ ```
123
+
124
+ `--stdin-filename` should end in `.md` so textlint parses the input as Markdown.
125
+
126
+ ## Supported Options
127
+
128
+ Slopless forwards normal textlint file and execution options.
42
129
 
43
- Use standard textlint formatters:
130
+ Useful examples:
44
131
 
45
132
  ```bash
46
- npx textlint "docs/**/*.md" --format stylish
47
- npx textlint "docs/**/*.md" --format json
133
+ npx slopless "docs/**/*.md" --quiet
134
+ npx slopless --stdin --stdin-filename draft.md
135
+ npx slopless --no-color
48
136
  ```
49
137
 
138
+ Unsupported:
139
+
140
+ ```bash
141
+ npx slopless --format stylish
142
+ npx slopless -f json
143
+ ```
144
+
145
+ `--format` and `-f` are rejected because Slopless always emits JSON.
146
+
50
147
  ## What It Checks
51
148
 
149
+ Slopless checks for:
150
+
52
151
  - stock AI-style phrasing
53
152
  - empty or generic prose patterns
54
153
  - rhetorical filler
55
- - weak closers and lead-ins
154
+ - weak lead-ins and closers
56
155
  - hedge stacking
57
- - prohibited vocabulary
156
+ - prohibited or overused vocabulary
157
+ - cliches and corporate phrasing
158
+ - fake precision signals
58
159
  - readability and sentence metrics
59
160
  - Markdown style signals
60
161
 
61
- ## Requirements
162
+ ## Why It Exists
163
+
164
+ Generated prose often repeats the same rhetorical moves: vague contrast, empty emotional payoff, overconfident summaries, generic transitions, and formulaic conclusions.
165
+
166
+ General grammar tools are not aimed at those patterns. LLM review can catch them, but it is slower, non-deterministic, and harder to use as a stable CI gate.
167
+
168
+ Slopless keeps that layer deterministic. It gives projects a repeatable JSON report of known prose issues.
169
+
170
+ ## Advanced Textlint Use
171
+
172
+ The package also exports a textlint preset for users who already run textlint directly.
173
+
174
+ `.textlintrc.json`:
175
+
176
+ ```json
177
+ {
178
+ "rules": {
179
+ "preset-slopless": true
180
+ }
181
+ }
182
+ ```
183
+
184
+ Direct textlint use:
185
+
186
+ ```bash
187
+ npx textlint "docs/**/*.md"
188
+ ```
62
189
 
63
- - Node.js 20 or newer
64
- - textlint 15
190
+ Most users should use `npx slopless` instead.
package/dist/cli.js ADDED
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { cli } from "textlint/lib/src/cli.js";
5
+ const DEFAULT_TARGET = "**/*.md";
6
+ const FORMAT_FLAGS = new Set(["--format", "-f"]);
7
+ const HELP_FLAGS = new Set(["--help", "-h"]);
8
+ const VERSION_FLAGS = new Set(["--version", "-v"]);
9
+ const VERSION = "0.2.1";
10
+ const HELP_TEXT = `Slopless checks Markdown prose for deterministic slop signals and writes JSON.
11
+
12
+ Install:
13
+ npm install -D slopless
14
+
15
+ Run:
16
+ npx slopless
17
+ npx slopless "docs/**/*.md"
18
+ npx slopless draft.md > slopless.json
19
+
20
+ Package script:
21
+ {
22
+ "scripts": {
23
+ "lint:prose": "slopless"
24
+ }
25
+ }
26
+
27
+ Default behavior:
28
+ - If no file path is passed, Slopless checks **/*.md.
29
+ - Output is always JSON.
30
+ - Exit 0 means no findings.
31
+ - Exit 1 means Slopless found prose issues.
32
+ - Exit 2 means the command failed before linting.
33
+ - No .textlintrc.json is required.
34
+ - No separate textlint install is required.
35
+
36
+ What it is for:
37
+ Slopless is for deterministic prose checks in CI, local scripts, and review
38
+ pipelines. It catches repeated AI-style phrasing, empty claims, rhetorical
39
+ filler, weak lead-ins and closers, hedge stacking, readability problems, and
40
+ Markdown style signals.
41
+
42
+ What it is not for:
43
+ Slopless does not rewrite text, check facts, judge taste, or replace human
44
+ editing. It reports concrete rule findings that another tool or person can
45
+ review.
46
+
47
+ Useful forms:
48
+ npx slopless --stdin --stdin-filename draft.md
49
+ npx slopless "docs/**/*.md" > slopless.json
50
+ npx slopless "docs/**/*.md" --quiet
51
+
52
+ Unsupported:
53
+ --format and -f are rejected. JSON is the only output format.
54
+ `;
55
+ function hasFormatOverride(args) {
56
+ return args.some((arg, index) => FORMAT_FLAGS.has(arg) ||
57
+ arg.startsWith("--format=") ||
58
+ (index > 0 && FORMAT_FLAGS.has(args[index - 1] ?? "")));
59
+ }
60
+ function hasFlag(args, flags) {
61
+ return args.some((arg) => flags.has(arg));
62
+ }
63
+ function hasFileTarget(args) {
64
+ return args.some((arg) => !arg.startsWith("-"));
65
+ }
66
+ function packageNodeModules() {
67
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
68
+ return resolve(packageRoot, "..");
69
+ }
70
+ async function main() {
71
+ const userArgs = process.argv.slice(2);
72
+ if (hasFlag(userArgs, HELP_FLAGS)) {
73
+ process.stdout.write(HELP_TEXT);
74
+ return 0;
75
+ }
76
+ if (hasFlag(userArgs, VERSION_FLAGS)) {
77
+ process.stdout.write(`${VERSION}\n`);
78
+ return 0;
79
+ }
80
+ if (hasFormatOverride(userArgs)) {
81
+ process.stderr.write("slopless always writes JSON output. Remove --format / -f.\n");
82
+ return 2;
83
+ }
84
+ const args = [
85
+ "node",
86
+ "slopless",
87
+ "--preset",
88
+ "slopless",
89
+ "--rules-base-directory",
90
+ packageNodeModules(),
91
+ "--format",
92
+ "json",
93
+ ...userArgs
94
+ ];
95
+ if (!hasFileTarget(userArgs)) {
96
+ args.push(DEFAULT_TARGET);
97
+ }
98
+ return cli.execute(args);
99
+ }
100
+ process.exitCode = await main();
101
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAE9C,MAAM,cAAc,GAAG,SAAS,CAAC;AACjC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;AACjD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;AAC7C,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC;AACnD,MAAM,OAAO,GAAG,OAAO,CAAC;AACxB,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4CjB,CAAC;AAEF,SAAS,iBAAiB,CAAC,IAAuB;IAChD,OAAO,IAAI,CAAC,IAAI,CACd,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CACb,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC;QACrB,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC;QAC3B,CAAC,KAAK,GAAG,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CACzD,CAAC;AACJ,CAAC;AAED,SAAS,OAAO,CAAC,IAAuB,EAAE,KAA0B;IAClE,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,aAAa,CAAC,IAAuB;IAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,SAAS,kBAAkB;IACzB,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAErE,OAAO,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEvC,IAAI,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,EAAE,CAAC;QACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;QACrC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6DAA6D,CAC9D,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,IAAI,GAAG;QACX,MAAM;QACN,UAAU;QACV,UAAU;QACV,UAAU;QACV,wBAAwB;QACxB,kBAAkB,EAAE;QACpB,UAAU;QACV,MAAM;QACN,GAAG,QAAQ;KACZ,CAAC;IAEF,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC5B,CAAC;IAED,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,OAAO,CAAC,QAAQ,GAAG,MAAM,IAAI,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopless",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Deterministic textlint rules for detecting slop in prose.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -13,6 +13,7 @@
13
13
  "url": "https://github.com/agent-quality-controls/slopless/issues"
14
14
  },
15
15
  "type": "module",
16
+ "main": "./dist/index.js",
16
17
  "engines": {
17
18
  "node": ">=20.0.0"
18
19
  },
@@ -30,6 +31,9 @@
30
31
  "dist",
31
32
  "README.md"
32
33
  ],
34
+ "bin": {
35
+ "slopless": "dist/cli.js"
36
+ },
33
37
  "exports": {
34
38
  ".": "./dist/index.js",
35
39
  "./families/metrics/avg-sentence-length": "./dist/families/metrics/avg-sentence-length.js",
@@ -82,18 +86,17 @@
82
86
  },
83
87
  "dependencies": {
84
88
  "@lunarisapp/readability": "^1.1.0",
89
+ "@textlint/ast-node-types": "15.6.1",
90
+ "@textlint/types": "15.6.1",
85
91
  "sentence-splitter": "5.0.1",
92
+ "textlint": "15.6.1",
86
93
  "textlint-rule-helper": "2.5.0",
87
94
  "textlint-util-to-string": "3.3.4"
88
95
  },
89
- "peerDependencies": {
90
- "textlint": "^15.6.1"
91
- },
92
96
  "devDependencies": {
93
97
  "@double-great/stylelint-a11y": "3.4.12",
94
98
  "@eslint-community/eslint-plugin-eslint-comments": "4.7.1",
95
- "@textlint/ast-node-types": "15.6.1",
96
- "@textlint/types": "15.6.1",
99
+ "@types/node": "^24.10.1",
97
100
  "@typescript-eslint/eslint-plugin": "8.59.3",
98
101
  "@typescript-eslint/parser": "8.59.3",
99
102
  "cspell": "10.0.0",
@@ -108,7 +111,6 @@
108
111
  "stylelint": "17.11.0",
109
112
  "stylelint-config-standard": "40.0.0",
110
113
  "stylelint-config-tailwindcss": "1.0.1",
111
- "textlint": "15.6.1",
112
114
  "type-coverage": "2.29.7",
113
115
  "typescript": "5.9.3"
114
116
  }