nollm 0.4.0 → 0.6.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 +28 -8
- package/package.json +1 -1
- package/src/cli.js +80 -13
- package/src/rules.js +10 -0
package/README.md
CHANGED
|
@@ -52,14 +52,16 @@ through a row of `..`.
|
|
|
52
52
|
Ignore patterns from the config describe the project, so they apply under the
|
|
53
53
|
current directory and leave paths outside it alone.
|
|
54
54
|
|
|
55
|
-
| Option
|
|
56
|
-
|
|
|
57
|
-
| `--jobs <n>`
|
|
58
|
-
| `--config <path>`
|
|
59
|
-
| `--diff <ref>`
|
|
60
|
-
| `--no-git`
|
|
61
|
-
| `--
|
|
62
|
-
| `--
|
|
55
|
+
| Option | Effect |
|
|
56
|
+
| ------------------------- | ------------------------------------------------------------------ |
|
|
57
|
+
| `--jobs <n>` | Number of worker threads. Defaults to the CPU count. |
|
|
58
|
+
| `--config <path>` | Config file to use. |
|
|
59
|
+
| `--diff <ref>` | Check only the lines this branch adds or changes since `<ref>`. |
|
|
60
|
+
| `--no-git` | Do not ask git for the file list. Read `.gitignore` files instead. |
|
|
61
|
+
| `--stdin` | Read the text from stdin. The path `-` does the same. |
|
|
62
|
+
| `--stdin-filename <name>` | Check stdin as if it were this file. Defaults to `stdin.md`. |
|
|
63
|
+
| `--quiet` | Print only the summary. |
|
|
64
|
+
| `--list-rules` | Print every rule and exit. |
|
|
63
65
|
|
|
64
66
|
The exit code is 1 when there are findings, and 2 on a usage error.
|
|
65
67
|
|
|
@@ -80,6 +82,24 @@ src/index.js
|
|
|
80
82
|
4 problems in 2 files (5 files checked, 0.07s)
|
|
81
83
|
```
|
|
82
84
|
|
|
85
|
+
## Checking text from stdin
|
|
86
|
+
|
|
87
|
+
Pass `-` to check text that is not in a file, such as a draft or a commit message:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
pbpaste | nollm -
|
|
91
|
+
git log -1 --format=%B | nollm -
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Stdin is checked as markdown. To check it as another type, name a file.
|
|
95
|
+
The name picks the language and labels the report. No file is read:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
git show main:src/index.js | nollm --stdin-filename src/index.js
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The config applies as usual. Stdin cannot be combined with paths or `--diff`.
|
|
102
|
+
|
|
83
103
|
## Checking only a pull request
|
|
84
104
|
|
|
85
105
|
A large codebase written before you added `nollm` has findings everywhere.
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { text as readAll } from "node:stream/consumers";
|
|
2
4
|
import { parseArgs, styleText } from "node:util";
|
|
5
|
+
import { check } from "./check.js";
|
|
6
|
+
import { findConfig, loadConfig } from "./config.js";
|
|
7
|
+
import { classify } from "./languages.js";
|
|
3
8
|
import { lint } from "./lint.js";
|
|
4
9
|
import { rules } from "./rules.js";
|
|
5
10
|
|
|
11
|
+
const STDIN_FILENAME = "stdin.md";
|
|
12
|
+
|
|
6
13
|
const HELP = `Usage: nollm [options] [paths...]
|
|
7
14
|
|
|
8
15
|
Checks files for LLMisms and prints each finding as soon as it is found.
|
|
9
16
|
Paths may be relative or absolute. Files that git ignores are skipped.
|
|
17
|
+
The path - reads the text from stdin instead.
|
|
10
18
|
|
|
11
19
|
Options:
|
|
12
20
|
--jobs, -j <n> Number of worker threads (default: cpu count)
|
|
13
21
|
--config <path> Config file (default: nollm.config.js in the current directory)
|
|
14
22
|
--diff <ref> Check only the lines this branch adds or changes since <ref>
|
|
15
23
|
--no-git Do not ask git for the file list. Read .gitignore files instead
|
|
24
|
+
--stdin Read the text from stdin. Same as the path -
|
|
25
|
+
--stdin-filename <name>
|
|
26
|
+
Check stdin as if it were this file (default: ${STDIN_FILENAME})
|
|
16
27
|
--quiet, -q Print only the summary
|
|
17
28
|
--list-rules Print every rule and exit
|
|
18
29
|
--version, -v Print the version and exit
|
|
@@ -21,13 +32,20 @@ Options:
|
|
|
21
32
|
Examples:
|
|
22
33
|
nollm docs/guide.md a path relative to the current directory
|
|
23
34
|
nollm /srv/site/docs/guide.md an absolute path
|
|
35
|
+
pbpaste | nollm - text from the clipboard, as markdown
|
|
36
|
+
git show HEAD:a.py | nollm --stdin-filename a.py
|
|
24
37
|
|
|
25
38
|
Exit code 1 when there are findings. Exit code 2 on a usage error.
|
|
26
39
|
`;
|
|
27
40
|
|
|
28
41
|
export async function main(
|
|
29
42
|
argv,
|
|
30
|
-
{
|
|
43
|
+
{
|
|
44
|
+
stdin = process.stdin,
|
|
45
|
+
stdout = process.stdout,
|
|
46
|
+
stderr = process.stderr,
|
|
47
|
+
cwd = process.cwd(),
|
|
48
|
+
} = {},
|
|
31
49
|
) {
|
|
32
50
|
let parsed;
|
|
33
51
|
try {
|
|
@@ -40,6 +58,8 @@ export async function main(
|
|
|
40
58
|
config: { type: "string" },
|
|
41
59
|
diff: { type: "string" },
|
|
42
60
|
git: { type: "boolean", default: true },
|
|
61
|
+
stdin: { type: "boolean", default: false },
|
|
62
|
+
"stdin-filename": { type: "string" },
|
|
43
63
|
quiet: { type: "boolean", short: "q", default: false },
|
|
44
64
|
"list-rules": { type: "boolean", default: false },
|
|
45
65
|
version: { type: "boolean", short: "v", default: false },
|
|
@@ -83,21 +103,44 @@ export async function main(
|
|
|
83
103
|
|
|
84
104
|
const paint = (style, text) => styleText(style, text, { stream: stdout });
|
|
85
105
|
const started = performance.now();
|
|
106
|
+
const fromStdin =
|
|
107
|
+
values.stdin || values["stdin-filename"] !== undefined || positionals.includes("-");
|
|
108
|
+
|
|
109
|
+
if (fromStdin) {
|
|
110
|
+
if (positionals.some((path) => path !== "-")) {
|
|
111
|
+
stderr.write("Paths cannot be combined with stdin. Check one or the other\n");
|
|
112
|
+
return 2;
|
|
113
|
+
}
|
|
114
|
+
if (values.diff !== undefined) {
|
|
115
|
+
stderr.write("--diff cannot be combined with stdin, since stdin has no git history\n");
|
|
116
|
+
return 2;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const onResult = (result) => {
|
|
121
|
+
if (values.quiet || result.findings.length === 0) return;
|
|
122
|
+
stdout.write(formatFile(result.file, result.findings, paint));
|
|
123
|
+
};
|
|
86
124
|
|
|
87
125
|
let summary;
|
|
88
126
|
try {
|
|
89
|
-
summary =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
127
|
+
summary = fromStdin
|
|
128
|
+
? await lintStdin({
|
|
129
|
+
stdin,
|
|
130
|
+
file: values["stdin-filename"] ?? STDIN_FILENAME,
|
|
131
|
+
cwd,
|
|
132
|
+
configPath: values.config,
|
|
133
|
+
onResult,
|
|
134
|
+
})
|
|
135
|
+
: await lint({
|
|
136
|
+
roots: positionals.length > 0 ? positionals : ["."],
|
|
137
|
+
cwd,
|
|
138
|
+
configPath: values.config,
|
|
139
|
+
git: values.git,
|
|
140
|
+
diff: values.diff,
|
|
141
|
+
jobs,
|
|
142
|
+
onResult,
|
|
143
|
+
});
|
|
101
144
|
} catch (error) {
|
|
102
145
|
stderr.write(`${error.message}\n`);
|
|
103
146
|
return 2;
|
|
@@ -112,6 +155,30 @@ export async function main(
|
|
|
112
155
|
return summary.findings > 0 ? 1 : 0;
|
|
113
156
|
}
|
|
114
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Checks the text on stdin as if it were the named file.
|
|
160
|
+
*
|
|
161
|
+
* The name picks the language and labels the report. No file is read.
|
|
162
|
+
* Returns the same summary as lint, for one file.
|
|
163
|
+
*/
|
|
164
|
+
async function lintStdin({ stdin, file, cwd, configPath, onResult }) {
|
|
165
|
+
if (!classify(file)) {
|
|
166
|
+
throw new Error(`nollm does not know how to check "${file}". Pass --stdin-filename a.md`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const resolvedConfig = configPath ? resolve(cwd, configPath) : await findConfig(cwd);
|
|
170
|
+
const config = await loadConfig(resolvedConfig);
|
|
171
|
+
const findings = check(file, await readAll(stdin), config.rules);
|
|
172
|
+
|
|
173
|
+
onResult({ file, findings, skipped: null });
|
|
174
|
+
return {
|
|
175
|
+
files: 1,
|
|
176
|
+
checked: 1,
|
|
177
|
+
findings: findings.length,
|
|
178
|
+
filesWithFindings: findings.length > 0 ? 1 : 0,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
115
182
|
/**
|
|
116
183
|
* One block per file:
|
|
117
184
|
*
|
package/src/rules.js
CHANGED
|
@@ -270,6 +270,16 @@ const PR_TALK = [
|
|
|
270
270
|
"existing behavior",
|
|
271
271
|
"existing behaviour",
|
|
272
272
|
"unchanged from",
|
|
273
|
+
// Explaining a step that is not there
|
|
274
|
+
"nothing to disable",
|
|
275
|
+
"nothing to turn off",
|
|
276
|
+
"nothing to uninstall",
|
|
277
|
+
"nothing to revert",
|
|
278
|
+
"nothing to opt out of",
|
|
279
|
+
"no need to disable",
|
|
280
|
+
"no need to turn off",
|
|
281
|
+
"no need to uninstall",
|
|
282
|
+
"no need to revert",
|
|
273
283
|
// Leaving things as they were
|
|
274
284
|
"keeping it off",
|
|
275
285
|
"keeping it on",
|