llm-slop-detector 0.5.0 → 0.7.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/CHANGELOG.md +40 -0
- package/out/extension.js +136 -6
- package/package.json +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.0](https://github.com/mandakan/llm-slop-detector/compare/llm-slop-detector-v0.6.2...llm-slop-detector-v0.7.0) (2026-04-24)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* mark files with slop in the explorer ([#62](https://github.com/mandakan/llm-slop-detector/issues/62)) ([ff4e240](https://github.com/mandakan/llm-slop-detector/commit/ff4e2402a1aee52032061b922214756586cd0b01))
|
|
9
|
+
* publish releases to Open VSX Registry ([#60](https://github.com/mandakan/llm-slop-detector/issues/60)) ([b572f87](https://github.com/mandakan/llm-slop-detector/commit/b572f8770139f773e19f9038ef377016cd64a4a4))
|
|
10
|
+
|
|
11
|
+
## [0.6.2](https://github.com/mandakan/llm-slop-detector/compare/llm-slop-detector-v0.6.1...llm-slop-detector-v0.6.2) (2026-04-24)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* run npm publish via npx instead of upgrading in place ([#58](https://github.com/mandakan/llm-slop-detector/issues/58)) ([ae3a370](https://github.com/mandakan/llm-slop-detector/commit/ae3a3702cc9ccb5014f06905b8ae7d4158a308f3))
|
|
17
|
+
|
|
18
|
+
## [0.6.1](https://github.com/mandakan/llm-slop-detector/compare/llm-slop-detector-v0.6.0...llm-slop-detector-v0.6.1) (2026-04-24)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* publish to npm from release workflow via OIDC ([#56](https://github.com/mandakan/llm-slop-detector/issues/56)) ([f249939](https://github.com/mandakan/llm-slop-detector/commit/f24993950227a7b1ee9a2a9af48591f4235e2cf0))
|
|
24
|
+
|
|
25
|
+
## [0.6.0](https://github.com/mandakan/llm-slop-detector/compare/llm-slop-detector-v0.5.0...llm-slop-detector-v0.6.0) (2026-04-24)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* add .slopignore and llmSlopDetector.exclude for file-glob skips ([#46](https://github.com/mandakan/llm-slop-detector/issues/46)) ([836f081](https://github.com/mandakan/llm-slop-detector/commit/836f08165405311d4adc1457d9bf6e03ea37a4ef))
|
|
31
|
+
* add llmSlopDetector.severityOverrides for per-rule tuning ([#47](https://github.com/mandakan/llm-slop-detector/issues/47)) ([d4d8750](https://github.com/mandakan/llm-slop-detector/commit/d4d87505ccd7593ea77cceb03be5c1f199c46e2a)), closes [#37](https://github.com/mandakan/llm-slop-detector/issues/37)
|
|
32
|
+
* add Scan workspace command ([#53](https://github.com/mandakan/llm-slop-detector/issues/53)) ([26e3270](https://github.com/mandakan/llm-slop-detector/commit/26e32708d9903efc7a87632f87495eacbfaf9114))
|
|
33
|
+
* expose detector as stdio MCP server for agent self-linting ([#51](https://github.com/mandakan/llm-slop-detector/issues/51)) ([bdd7e29](https://github.com/mandakan/llm-slop-detector/commit/bdd7e29fcd0e067dc5086997ac476193eb7a0da4))
|
|
34
|
+
* publish CLI to npm ([#55](https://github.com/mandakan/llm-slop-detector/issues/55)) ([fe529e7](https://github.com/mandakan/llm-slop-detector/commit/fe529e764731bafad2553fc1268c2696741e4fcd))
|
|
35
|
+
* scan git commit messages and SCM input box ([#44](https://github.com/mandakan/llm-slop-detector/issues/44)) ([3e3196e](https://github.com/mandakan/llm-slop-detector/commit/3e3196e7ebc14614d27eee35c3c051effb176591)), closes [#40](https://github.com/mandakan/llm-slop-detector/issues/40)
|
|
36
|
+
* ship JSON Schema for .llmsloprc.json ([#45](https://github.com/mandakan/llm-slop-detector/issues/45)) ([96c2e0b](https://github.com/mandakan/llm-slop-detector/commit/96c2e0bd0bce338333d24fc71771209a62c19d0c)), closes [#36](https://github.com/mandakan/llm-slop-detector/issues/36)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
### Performance Improvements
|
|
40
|
+
|
|
41
|
+
* debounce document scanning on keystrokes ([#42](https://github.com/mandakan/llm-slop-detector/issues/42)) ([0ddb4ca](https://github.com/mandakan/llm-slop-detector/commit/0ddb4cacfc9205ac44bb9a997bbf5bea3c005731)), closes [#41](https://github.com/mandakan/llm-slop-detector/issues/41)
|
|
42
|
+
|
|
3
43
|
## [0.5.0](https://github.com/mandakan/llm-slop-detector/compare/llm-slop-detector-v0.4.0...llm-slop-detector-v0.5.0) (2026-04-23)
|
|
4
44
|
|
|
5
45
|
|
package/out/extension.js
CHANGED
|
@@ -84,6 +84,112 @@ function isIgnoredDocument(doc) {
|
|
|
84
84
|
// Findings keyed by document URI, stashed during scan so the hover provider
|
|
85
85
|
// can recover rule metadata (pattern, matched char) without rescanning.
|
|
86
86
|
const FINDINGS_BY_URI = new Map();
|
|
87
|
+
// Explorer decoration provider. Marks files known to contain slop so users
|
|
88
|
+
// can see where work is pending without having to open each file. Populated
|
|
89
|
+
// lazily from two sources: documents scanned while open, and results from
|
|
90
|
+
// the workspace-scan command. Folders get the color via `propagate: true`;
|
|
91
|
+
// VS Code only re-queries ancestors when we fire events for them, so the
|
|
92
|
+
// provider walks up to the workspace root on every state change.
|
|
93
|
+
class SlopDecorationProvider {
|
|
94
|
+
emitter = new vscode.EventEmitter();
|
|
95
|
+
onDidChangeFileDecorations = this.emitter.event;
|
|
96
|
+
slopyPaths = new Set();
|
|
97
|
+
setFileState(uri, hasSlop) {
|
|
98
|
+
if (uri.scheme !== 'file')
|
|
99
|
+
return;
|
|
100
|
+
const p = uri.fsPath;
|
|
101
|
+
const had = this.slopyPaths.has(p);
|
|
102
|
+
if (had === hasSlop)
|
|
103
|
+
return;
|
|
104
|
+
if (hasSlop)
|
|
105
|
+
this.slopyPaths.add(p);
|
|
106
|
+
else
|
|
107
|
+
this.slopyPaths.delete(p);
|
|
108
|
+
this.emitter.fire(this.urisForPathAndAncestors(p));
|
|
109
|
+
}
|
|
110
|
+
applyScanResults(slopyPaths, scannedPaths) {
|
|
111
|
+
const next = new Set(slopyPaths);
|
|
112
|
+
const changed = [];
|
|
113
|
+
for (const p of scannedPaths) {
|
|
114
|
+
const shouldMark = next.has(p);
|
|
115
|
+
const isMarked = this.slopyPaths.has(p);
|
|
116
|
+
if (shouldMark === isMarked)
|
|
117
|
+
continue;
|
|
118
|
+
if (shouldMark)
|
|
119
|
+
this.slopyPaths.add(p);
|
|
120
|
+
else
|
|
121
|
+
this.slopyPaths.delete(p);
|
|
122
|
+
for (const u of this.urisForPathAndAncestors(p))
|
|
123
|
+
changed.push(u);
|
|
124
|
+
}
|
|
125
|
+
if (changed.length > 0)
|
|
126
|
+
this.emitter.fire(changed);
|
|
127
|
+
}
|
|
128
|
+
forgetPath(fsPath) {
|
|
129
|
+
if (!this.slopyPaths.delete(fsPath))
|
|
130
|
+
return;
|
|
131
|
+
this.emitter.fire(this.urisForPathAndAncestors(fsPath));
|
|
132
|
+
}
|
|
133
|
+
clearAll() {
|
|
134
|
+
if (this.slopyPaths.size === 0)
|
|
135
|
+
return;
|
|
136
|
+
this.slopyPaths.clear();
|
|
137
|
+
this.emitter.fire(undefined);
|
|
138
|
+
}
|
|
139
|
+
provideFileDecoration(uri) {
|
|
140
|
+
if (uri.scheme !== 'file')
|
|
141
|
+
return;
|
|
142
|
+
if (this.slopyPaths.has(uri.fsPath)) {
|
|
143
|
+
return {
|
|
144
|
+
badge: 'S',
|
|
145
|
+
tooltip: 'Contains LLM slop',
|
|
146
|
+
color: new vscode.ThemeColor('list.warningForeground'),
|
|
147
|
+
propagate: true,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// Folders: return decoration if any tracked file lives under this path.
|
|
151
|
+
// The tree rarely has more than a handful of slopy files, so an O(n)
|
|
152
|
+
// scan per folder query is fine and avoids maintaining a separate index.
|
|
153
|
+
const folderPrefix = uri.fsPath + path.sep;
|
|
154
|
+
for (const p of this.slopyPaths) {
|
|
155
|
+
if (p.startsWith(folderPrefix)) {
|
|
156
|
+
return {
|
|
157
|
+
tooltip: 'Contains LLM slop',
|
|
158
|
+
color: new vscode.ThemeColor('list.warningForeground'),
|
|
159
|
+
propagate: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
urisForPathAndAncestors(fsPath) {
|
|
166
|
+
const uris = [vscode.Uri.file(fsPath)];
|
|
167
|
+
const folder = vscode.workspace.workspaceFolders?.find(f => fsPath === f.uri.fsPath || fsPath.startsWith(f.uri.fsPath + path.sep));
|
|
168
|
+
if (!folder)
|
|
169
|
+
return uris;
|
|
170
|
+
let cur = path.dirname(fsPath);
|
|
171
|
+
const root = folder.uri.fsPath;
|
|
172
|
+
while (cur.length >= root.length && cur.startsWith(root)) {
|
|
173
|
+
uris.push(vscode.Uri.file(cur));
|
|
174
|
+
if (cur === root)
|
|
175
|
+
break;
|
|
176
|
+
const parent = path.dirname(cur);
|
|
177
|
+
if (parent === cur)
|
|
178
|
+
break;
|
|
179
|
+
cur = parent;
|
|
180
|
+
}
|
|
181
|
+
return uris;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
let DECORATION_PROVIDER;
|
|
185
|
+
function updateDecorationForDocument(doc, findings) {
|
|
186
|
+
if (!DECORATION_PROVIDER || doc.uri.scheme !== 'file')
|
|
187
|
+
return;
|
|
188
|
+
const cfg = vscode.workspace.getConfiguration('llmSlopDetector');
|
|
189
|
+
if (!cfg.get('decorateExplorer', true))
|
|
190
|
+
return;
|
|
191
|
+
DECORATION_PROVIDER.setFileState(doc.uri, findings.length > 0);
|
|
192
|
+
}
|
|
87
193
|
const PENDING_REFRESH = new Map();
|
|
88
194
|
function cancelPendingRefresh(uriKey) {
|
|
89
195
|
const p = PENDING_REFRESH.get(uriKey);
|
|
@@ -217,9 +323,12 @@ function activate(context) {
|
|
|
217
323
|
if (!enabled || !SUPPORTED_LANGS.has(doc.languageId) || isIgnoredDocument(doc)) {
|
|
218
324
|
collection.delete(doc.uri);
|
|
219
325
|
FINDINGS_BY_URI.delete(doc.uri.toString());
|
|
326
|
+
updateDecorationForDocument(doc, []);
|
|
220
327
|
return;
|
|
221
328
|
}
|
|
222
|
-
|
|
329
|
+
const diags = scanDocument(doc);
|
|
330
|
+
collection.set(doc.uri, diags);
|
|
331
|
+
updateDecorationForDocument(doc, diags);
|
|
223
332
|
};
|
|
224
333
|
// Leading+trailing debounce: first change after idle triggers an immediate
|
|
225
334
|
// scan so feedback stays snappy; subsequent changes within the window
|
|
@@ -297,9 +406,16 @@ function activate(context) {
|
|
|
297
406
|
RULES = (0, rules_1.loadRules)(context.extensionUri);
|
|
298
407
|
reloadIgnore();
|
|
299
408
|
rebuildSupportedLangs();
|
|
409
|
+
const cfg = vscode.workspace.getConfiguration('llmSlopDetector');
|
|
410
|
+
const decorate = cfg.get('decorateExplorer', true);
|
|
411
|
+
const enabled = cfg.get('enabled', true);
|
|
412
|
+
if (!decorate || !enabled)
|
|
413
|
+
DECORATION_PROVIDER?.clearAll();
|
|
300
414
|
vscode.workspace.textDocuments.forEach(refresh);
|
|
301
415
|
updateStatus();
|
|
302
416
|
};
|
|
417
|
+
DECORATION_PROVIDER = new SlopDecorationProvider();
|
|
418
|
+
context.subscriptions.push(vscode.window.registerFileDecorationProvider(DECORATION_PROVIDER));
|
|
303
419
|
reloadRules();
|
|
304
420
|
// Live-reload when a local .llmsloprc.json is created/changed/deleted
|
|
305
421
|
// anywhere in the workspace. The loader itself only reads the files at
|
|
@@ -313,7 +429,13 @@ function activate(context) {
|
|
|
313
429
|
collection.delete(doc.uri);
|
|
314
430
|
FINDINGS_BY_URI.delete(key);
|
|
315
431
|
updateStatus();
|
|
316
|
-
}), vscode.workspace.onDidChangeWorkspaceFolders(reloadRules), vscode.workspace.
|
|
432
|
+
}), vscode.workspace.onDidChangeWorkspaceFolders(reloadRules), vscode.workspace.onDidDeleteFiles(e => {
|
|
433
|
+
for (const uri of e.files)
|
|
434
|
+
DECORATION_PROVIDER?.forgetPath(uri.fsPath);
|
|
435
|
+
}), vscode.workspace.onDidRenameFiles(e => {
|
|
436
|
+
for (const { oldUri } of e.files)
|
|
437
|
+
DECORATION_PROVIDER?.forgetPath(oldUri.fsPath);
|
|
438
|
+
}), vscode.workspace.onDidGrantWorkspaceTrust(reloadRules), vscode.window.onDidChangeActiveTextEditor(() => updateStatus()), vscode.languages.onDidChangeDiagnostics(() => updateStatus()), vscode.workspace.onDidChangeConfiguration(e => {
|
|
317
439
|
if (e.affectsConfiguration('llmSlopDetector'))
|
|
318
440
|
reloadRules();
|
|
319
441
|
}), vscode.languages.registerCodeActionsProvider(CODE_ACTION_SELECTORS, new SlopCodeActionProvider(), { providedCodeActionKinds: SlopCodeActionProvider.providedCodeActionKinds }), vscode.languages.registerHoverProvider(CODE_ACTION_SELECTORS, new SlopHoverProvider()), vscode.commands.registerCommand('llmSlopDetector.toggle', async () => {
|
|
@@ -450,7 +572,7 @@ async function scanWorkspace() {
|
|
|
450
572
|
return;
|
|
451
573
|
}
|
|
452
574
|
const extensions = buildScanExtensionMap();
|
|
453
|
-
const
|
|
575
|
+
const result = await vscode.window.withProgress({
|
|
454
576
|
location: vscode.ProgressLocation.Notification,
|
|
455
577
|
title: 'LLM Slop Detector: scanning workspace',
|
|
456
578
|
cancellable: true,
|
|
@@ -471,8 +593,9 @@ async function scanWorkspace() {
|
|
|
471
593
|
}
|
|
472
594
|
if (token.isCancellationRequested)
|
|
473
595
|
return undefined;
|
|
596
|
+
const scannedPaths = new Set(targets.map(t => t.absPath));
|
|
474
597
|
if (targets.length === 0)
|
|
475
|
-
return [];
|
|
598
|
+
return { hits: [], scannedPaths };
|
|
476
599
|
// Use the in-memory text of any open document so unsaved changes are
|
|
477
600
|
// reflected in the scan, falling back to the on-disk version otherwise.
|
|
478
601
|
const openText = new Map();
|
|
@@ -518,10 +641,17 @@ async function scanWorkspace() {
|
|
|
518
641
|
}));
|
|
519
642
|
if (token.isCancellationRequested)
|
|
520
643
|
return undefined;
|
|
521
|
-
return out;
|
|
644
|
+
return { hits: out, scannedPaths };
|
|
522
645
|
});
|
|
523
|
-
if (
|
|
646
|
+
if (result === undefined)
|
|
524
647
|
return;
|
|
648
|
+
const { hits, scannedPaths } = result;
|
|
649
|
+
if (cfg.get('decorateExplorer', true) && DECORATION_PROVIDER) {
|
|
650
|
+
const slopyAbsPaths = new Set();
|
|
651
|
+
for (const h of hits)
|
|
652
|
+
slopyAbsPaths.add(h.uri.fsPath);
|
|
653
|
+
DECORATION_PROVIDER.applyScanResults(slopyAbsPaths, scannedPaths);
|
|
654
|
+
}
|
|
525
655
|
if (hits.length === 0) {
|
|
526
656
|
vscode.window.showInformationMessage('LLM Slop Detector: no findings in the workspace.');
|
|
527
657
|
return;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "llm-slop-detector",
|
|
3
3
|
"displayName": "LLM Slop Detector",
|
|
4
4
|
"description": "Highlights invisible Unicode, AI-style punctuation, and telltale LLM phrases in markdown and plain text.",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.7.0",
|
|
6
6
|
"publisher": "thias-se",
|
|
7
7
|
"engines": {
|
|
8
8
|
"vscode": "^1.95.0"
|
|
@@ -132,6 +132,11 @@
|
|
|
132
132
|
"default": [],
|
|
133
133
|
"description": "File-glob patterns to skip, using .gitignore syntax. Merged with patterns from a .slopignore file at the workspace root. Example: [\"CHANGELOG.md\", \"docs/generated/**\"]. Use !pattern to re-include after a broader ignore."
|
|
134
134
|
},
|
|
135
|
+
"llmSlopDetector.decorateExplorer": {
|
|
136
|
+
"type": "boolean",
|
|
137
|
+
"default": true,
|
|
138
|
+
"description": "Mark files in the explorer that are known to contain LLM slop. Updates as you open scanned files; run 'Scan workspace' to populate marks for files you haven't opened."
|
|
139
|
+
},
|
|
135
140
|
"llmSlopDetector.debounceMs": {
|
|
136
141
|
"type": "number",
|
|
137
142
|
"default": 150,
|