nollm 0.4.0 → 0.5.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.
Files changed (3) hide show
  1. package/README.md +28 -8
  2. package/package.json +1 -1
  3. package/src/cli.js +80 -13
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 | 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
- | `--quiet` | Print only the summary. |
62
- | `--list-rules` | Print every rule and exit. |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nollm",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "lint against LLMisms in your codebase",
5
5
  "keywords": [
6
6
  "comments",
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
- { stdout = process.stdout, stderr = process.stderr, cwd = process.cwd() } = {},
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 = await lint({
90
- roots: positionals.length > 0 ? positionals : ["."],
91
- cwd,
92
- configPath: values.config,
93
- git: values.git,
94
- diff: values.diff,
95
- jobs,
96
- onResult(result) {
97
- if (values.quiet || result.findings.length === 0) return;
98
- stdout.write(formatFile(result.file, result.findings, paint));
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
  *