llm-slop-detector 0.6.2 → 0.8.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 +20 -0
- package/README.md +43 -1
- package/builtin-packs/deepseek.json +50 -0
- package/builtin-packs/gemini.json +62 -0
- package/builtin-packs/grok.json +58 -0
- package/builtin-packs/llama.json +55 -0
- package/builtin-packs/qwen.json +50 -0
- package/extension-browser/build.mjs +115 -0
- package/extension-browser/icons/icon-128.png +0 -0
- package/extension-browser/icons/icon-16.png +0 -0
- package/extension-browser/icons/icon-32.png +0 -0
- package/extension-browser/icons/icon-48.png +0 -0
- package/extension-browser/manifest.json +53 -0
- package/out/cli.js +3 -2
- package/out/core/rules.js +5 -51
- package/out/extension.js +136 -6
- package/out/mcp.js +3 -2
- package/out/node/ruleLoader.js +63 -0
- package/out/rules.js +2 -1
- package/package.json +26 -4
- package/web/build.mjs +93 -0
- package/web/index.html +67 -0
- package/web/main.ts +302 -0
- package/web/privacy.html +52 -0
- package/web/style.css +250 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.8.0](https://github.com/mandakan/llm-slop-detector/compare/llm-slop-detector-v0.7.0...llm-slop-detector-v0.8.0) (2026-04-24)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add model-family packs for Gemini, DeepSeek, Llama, Qwen, Grok ([#63](https://github.com/mandakan/llm-slop-detector/issues/63)) ([4a15b6f](https://github.com/mandakan/llm-slop-detector/commit/4a15b6f5304f7ae6ada263a2185a314c04d1bd75))
|
|
9
|
+
* add web playground for browser-based slop detection ([#67](https://github.com/mandakan/llm-slop-detector/issues/67)) ([773b7b0](https://github.com/mandakan/llm-slop-detector/commit/773b7b0866fa7edeec42a307d7ef2e0a462dd17a))
|
|
10
|
+
* browser extension (Chrome, Arc, Firefox) v1 ([#68](https://github.com/mandakan/llm-slop-detector/issues/68)) ([a640d80](https://github.com/mandakan/llm-slop-detector/commit/a640d80929b3cc112effd66c7720d333942761a0))
|
|
11
|
+
* contenteditable support in the browser extension ([#73](https://github.com/mandakan/llm-slop-detector/issues/73)) ([f024c9f](https://github.com/mandakan/llm-slop-detector/commit/f024c9fe27b24760ec958aeb4068c2f98c2ca2ef))
|
|
12
|
+
* inline overlay + fix-all + mark tooltips for browser extension ([#70](https://github.com/mandakan/llm-slop-detector/issues/70)) ([4b5e2d7](https://github.com/mandakan/llm-slop-detector/commit/4b5e2d744195a7549874d956f84bfd609d6cdf50))
|
|
13
|
+
* read-only page scan mode for the browser extension ([#72](https://github.com/mandakan/llm-slop-detector/issues/72)) ([1e41e55](https://github.com/mandakan/llm-slop-detector/commit/1e41e55e539f4b618efe5b6fbb6a957cfb9e1cb8))
|
|
14
|
+
|
|
15
|
+
## [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)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* 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))
|
|
21
|
+
* 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))
|
|
22
|
+
|
|
3
23
|
## [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)
|
|
4
24
|
|
|
5
25
|
|
package/README.md
CHANGED
|
@@ -7,11 +7,45 @@ A VS Code extension and CLI that flag invisible Unicode, AI-style punctuation, a
|
|
|
7
7
|
|
|
8
8
|

|
|
9
9
|
|
|
10
|
+
## Try it in the browser
|
|
11
|
+
|
|
12
|
+
No install needed -- paste text into the [web playground](https://mandakan.github.io/llm-slop-detector/) and get findings instantly. Everything runs in your browser; nothing is uploaded.
|
|
13
|
+
|
|
14
|
+
## Browser extension (Chrome, Arc, Firefox)
|
|
15
|
+
|
|
16
|
+
A WebExtension that scans `<textarea>`, text inputs, and `contenteditable` elements (Gmail compose, vanilla rich-text widgets) as you type on any webpage. Findings are highlighted inline with colour-coded marks -- textarea marks are tinted background, contenteditable marks are wavy underlines so they don't disturb reading of styled content. A small "N slop" badge next to each editor opens a per-finding popover with severity, reason, source pack, per-finding "Fix this" buttons, and a "Fix all chars" button that applies every deterministic character replacement (em dash, curly quotes, zero-width, etc.) at once. For contenteditables the fix goes through `execCommand('insertText')` so the host editor's undo stack still works (`Cmd+Z`). Runs entirely in the browser -- no network calls, no telemetry.
|
|
17
|
+
|
|
18
|
+
**Install from source (until stores are wired up):**
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm ci
|
|
22
|
+
npm run build:browser
|
|
23
|
+
# -> extension-browser-dist/ is the unpacked extension
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- **Chrome / Arc / Edge / Brave:** visit `chrome://extensions`, flip on "Developer mode", click "Load unpacked", pick `extension-browser-dist/`.
|
|
27
|
+
- **Firefox:** visit `about:debugging#/runtime/this-firefox`, click "Load Temporary Add-on...", pick `extension-browser-dist/manifest.json`. The add-on unloads when Firefox quits.
|
|
28
|
+
|
|
29
|
+
Toggle off globally (or per-site) via the extension's toolbar button. Pick rule packs in the options page.
|
|
30
|
+
|
|
31
|
+
### Read-only page scan
|
|
32
|
+
|
|
33
|
+
The toolbar popup also has a **Scan this page** button that highlights slop in any webpage you're reading -- a blog post, a Reddit thread, a LinkedIn update -- not just textareas. Findings are shown with a subtle wavy underline under the flagged word and a floating panel listing them in reading order, with click-to-jump and a clear-highlights button. The scan is a one-shot snapshot; re-click to rescan after the page changes.
|
|
34
|
+
|
|
35
|
+
Turn this off in the options page if you only want to lint your own writing. The scan is explicit (never auto-runs), local-only (no network), and one-shot per click (no background observers).
|
|
36
|
+
|
|
37
|
+
### Known limitations (v1)
|
|
38
|
+
|
|
39
|
+
- Rich editors with virtual-DOM reconcilers (Notion/Lexical, Substack/ProseMirror, LinkedIn/Draft.js, Twitter/X) strip our inline marks on their next render cycle. The badge count and popover still work, but inline underlines won't persist. Plain `contenteditable` (Gmail compose, vanilla rich-text widgets) is supported.
|
|
40
|
+
- Google Docs uses a canvas-based renderer with no real DOM text; the extension can't see its contents and won't work there.
|
|
41
|
+
- Cross-origin iframes are invisible for the same-origin-policy reason.
|
|
42
|
+
- Inline phrase matches that straddle inline formatting (e.g. a word that's partially italicised) appear in the popover list but without an inline mark, because `Range.surroundContents` rejects ranges that cross element boundaries. Workaround: strip the formatting if you want the mark.
|
|
43
|
+
|
|
10
44
|
## Features
|
|
11
45
|
|
|
12
46
|
- Flags zero-width, BOM, non-breaking spaces, and other invisible Unicode that hides in text and wrecks diffs
|
|
13
47
|
- Flags AI-style punctuation: em and en dashes, curly quotes, horizontal ellipsis, angle quotes
|
|
14
|
-
- Configurable phrase rules: ~40 built-in core rules plus
|
|
48
|
+
- Configurable phrase rules: ~40 built-in core rules plus eleven opt-in packs (`academic`, `cliches`, `fiction`, `claudeisms`, `structural`, `security`, `gemini`, `deepseek`, `llama`, `qwen`, `grok`) totalling 500+ curated regex patterns
|
|
15
49
|
- Markdown-aware: skips fenced and inline code, link URLs, and YAML frontmatter so technical prose doesn't drown in false positives
|
|
16
50
|
- Inline-ignore comments (`<!-- slop-disable -->`, `<!-- slop-disable-next-line -->`, `<!-- slop-disable-line -->`) for one-off exceptions
|
|
17
51
|
- Hover over any flagged range for the rule selector plus a ready-to-copy `slop-disable-next-line` snippet
|
|
@@ -152,6 +186,14 @@ The core list is deliberately conservative: ~40 phrase rules covering the buzzwo
|
|
|
152
186
|
- **`structural`** -- structural LLM tells: "not X but Y" negation pivots, sycophantic line openers, "in this section we'll" meta-commentary, "at the end of the day" closers. Original content.
|
|
153
187
|
- **`security`** -- LLM-weaponized invisibles above the BMP: tag characters (U+E0020-U+E007F, used in ASCII-smuggler prompt injection) and variation selectors (U+FE00-U+FE0E + U+E0100-U+E01EF, used for arbitrary-data smuggling in emoji and CJK). Severity `error`. Skips U+FE0F (emoji presentation selector) to avoid false positives. Opt in if you copy-paste LLM output into files you don't fully trust.
|
|
154
188
|
|
|
189
|
+
Model-family packs flag mannerisms specific to one vendor's models. Mix and match:
|
|
190
|
+
|
|
191
|
+
- **`gemini`** -- Google Gemini / Bard: breakdown-happy structure (`here's a breakdown`, `let's break it down`), analogy openers (`think of it as`, `imagine a`), `I hope this helps` closers, and identity / knowledge-cutoff / professional-advice disclaimers.
|
|
192
|
+
- **`deepseek`** -- DeepSeek V3 / R1: `<think>` tag leaks, R1 reasoning tics leaking into final output (`Wait,`, `Hmm,`, `Let me reconsider`, `the user is asking`), DeepSeek special-token leaks (`<|begin_of_thought|>`, `<|EOT|>`).
|
|
193
|
+
- **`llama`** -- Meta Llama: `as an AI` identity disclaimers, Llama-Guard refusal boilerplate (`I cannot provide`, `it would not be appropriate`), and Llama2 / Llama3 chat-template token leaks (`[INST]`, `<|eot_id|>`, `<<SYS>>`, `<|python_tag|>`).
|
|
194
|
+
- **`qwen`** -- Alibaba Qwen / QwQ: `Sure!` / `Of course!` openers, Alibaba identity disclaimers, ChatML token leaks (`<|im_start|>`), Qwen-VL tokens (`<|vision_start|>`), and QwQ reasoning-phase tics.
|
|
195
|
+
- **`grok`** -- xAI Grok: `Look,` / `Here's the deal` openers, forced directness (`no BS`, `straight up`, `real talk`), edginess (`based`, `cope`, `galaxy-brained`), Hitchhiker's Guide references (`42`, `Don't panic`), and Elon/X name-drops.
|
|
196
|
+
|
|
155
197
|
Enable one or more in your settings:
|
|
156
198
|
|
|
157
199
|
```json
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pack:deepseek",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "DeepSeek V3/R1 mannerisms, most notably reasoning-trace tokens and thinking-phase tics that leak into finished output.",
|
|
5
|
+
"phrases": [
|
|
6
|
+
{ "pattern": "<\\s*/?\\s*think\\s*>", "reason": "DeepSeek R1 reasoning tag leak", "severity": "warning" },
|
|
7
|
+
{ "pattern": "<\\s*/?\\s*thinking\\s*>", "reason": "reasoning tag leak", "severity": "warning" },
|
|
8
|
+
{ "pattern": "<\\s*/?\\s*reasoning\\s*>", "reason": "reasoning tag leak", "severity": "warning" },
|
|
9
|
+
{ "pattern": "<\\s*/?\\s*reflection\\s*>", "reason": "reasoning tag leak", "severity": "warning" },
|
|
10
|
+
{ "pattern": "<\\|\\s*(begin|end)_of_thought\\s*\\|>", "reason": "DeepSeek thought delimiter leak", "severity": "warning" },
|
|
11
|
+
{ "pattern": "<\\|\\s*(begin|end)_of_solution\\s*\\|>", "reason": "DeepSeek solution delimiter leak", "severity": "warning" },
|
|
12
|
+
{ "pattern": "<\\|fim_(begin|hole|end)\\|>", "reason": "DeepSeek FIM token leak", "severity": "warning" },
|
|
13
|
+
{ "pattern": "<\\|EOT\\|>", "reason": "DeepSeek end-of-turn token leak", "severity": "warning" },
|
|
14
|
+
{ "pattern": "^\\s*wait,?\\s", "reason": "R1 reasoning tic leaking into output" },
|
|
15
|
+
{ "pattern": "^\\s*(but |so |and )?wait,\\s", "reason": "R1 self-correction leak" },
|
|
16
|
+
{ "pattern": "^\\s*hmm+,?\\s", "reason": "R1 reasoning tic leaking into output" },
|
|
17
|
+
{ "pattern": "^\\s*(ah|aha|oh),?\\s", "reason": "R1 reasoning-interjection leak" },
|
|
18
|
+
{ "pattern": "^\\s*okay,?\\s*(so|let'?s|let me|now)\\b", "reason": "R1 reasoning preamble leak" },
|
|
19
|
+
{ "pattern": "^\\s*alright,?\\s*(so|let'?s|let me|now)\\b", "reason": "R1 reasoning preamble leak" },
|
|
20
|
+
{ "pattern": "\\blet me (think|reason|work|figure) (about |through |out )?(this|it|that) (out |through )?(step[- ]by[- ]step|carefully|first)?\\b", "reason": "R1 meta-reasoning leak" },
|
|
21
|
+
{ "pattern": "\\blet me re[- ]?(examine|check|verify|think|evaluate|analyze|consider)\\b", "reason": "R1 self-correction leak" },
|
|
22
|
+
{ "pattern": "\\blet me double[- ]check\\b", "reason": "R1 self-correction leak" },
|
|
23
|
+
{ "pattern": "\\bactually,?\\s*wait\\b", "reason": "R1 self-correction leak" },
|
|
24
|
+
{ "pattern": "\\bon second thought\\b", "reason": "R1 self-correction leak" },
|
|
25
|
+
{ "pattern": "\\bhold on,?\\b", "reason": "R1 self-correction leak" },
|
|
26
|
+
{ "pattern": "\\bi need to (think|reconsider|double[- ]check|verify)\\b", "reason": "R1 meta-reasoning leak" },
|
|
27
|
+
{ "pattern": "\\bi should (think|reconsider|double[- ]check|verify|make sure)\\b", "reason": "R1 meta-reasoning leak" },
|
|
28
|
+
{ "pattern": "\\bthe user (is asking|wants|is looking for|is trying)\\b", "reason": "R1 internal-monologue leak" },
|
|
29
|
+
{ "pattern": "\\bthe user'?s (question|query|request|prompt|goal|intent)\\b", "reason": "R1 internal-monologue leak" },
|
|
30
|
+
{ "pattern": "\\bso the user (is asking|wants|is looking for|is trying|said|mentioned)\\b", "reason": "R1 internal-monologue leak" },
|
|
31
|
+
{ "pattern": "\\bfirst,? let me\\b", "reason": "R1 reasoning preamble" },
|
|
32
|
+
{ "pattern": "\\bfirst,? i'?ll\\b", "reason": "R1 reasoning preamble" },
|
|
33
|
+
{ "pattern": "\\bfirst,? i need to\\b", "reason": "R1 reasoning preamble" },
|
|
34
|
+
{ "pattern": "\\bstep[- ]by[- ]step\\b", "reason": "R1 reasoning preamble" },
|
|
35
|
+
{ "pattern": "\\blet'?s think step[- ]by[- ]step\\b", "reason": "R1 reasoning preamble" },
|
|
36
|
+
{ "pattern": "\\bi('?m| am) going to (think|reason|work) through\\b", "reason": "R1 meta-reasoning leak" },
|
|
37
|
+
{ "pattern": "\\bbreaking (it|this) down into\\b", "reason": "R1 decomposition tic" },
|
|
38
|
+
{ "pattern": "\\bto summarize my (thinking|reasoning|thoughts)\\b", "reason": "R1 self-summary leak" },
|
|
39
|
+
{ "pattern": "\\bputting (it|this) all together\\b", "reason": "R1 conclusion tic" },
|
|
40
|
+
{ "pattern": "\\bcrucially,?\\s", "reason": "DeepSeek emphasis tic" },
|
|
41
|
+
{ "pattern": "\\bnotably,?\\s", "reason": "DeepSeek emphasis tic" },
|
|
42
|
+
{ "pattern": "\\bof note,?\\s", "reason": "DeepSeek emphasis tic" },
|
|
43
|
+
{ "pattern": "\\bindeed,?\\s", "reason": "DeepSeek affirmation tic" },
|
|
44
|
+
{ "pattern": "\\bthe key (insight|observation|realization|idea) (here |is )?\\b", "reason": "DeepSeek conclusion tic" },
|
|
45
|
+
{ "pattern": "\\bthe (main|key|core|central) (point|takeaway|idea) (here |is )?\\b", "reason": "DeepSeek conclusion tic" },
|
|
46
|
+
{ "pattern": "\\bi'?m deepseek\\b", "reason": "DeepSeek identity disclaimer" },
|
|
47
|
+
{ "pattern": "\\bi am deepseek\\b", "reason": "DeepSeek identity disclaimer" },
|
|
48
|
+
{ "pattern": "\\bi'?m an (ai )?(assistant|language model) (created |developed |made |trained )?by deepseek\\b", "reason": "DeepSeek identity disclaimer" }
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pack:gemini",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Google Gemini (and legacy Bard) mannerisms: breakdown-happy structure, enthusiastic openers/closers, identity disclaimers.",
|
|
5
|
+
"phrases": [
|
|
6
|
+
{ "pattern": "\\bhere'?s (a |the |your )?(detailed |quick |brief |simple )?breakdown\\b", "reason": "Gemini structural tic" },
|
|
7
|
+
{ "pattern": "\\bhere'?s (a |the )?(quick |simple |brief )?rundown\\b", "reason": "Gemini structural tic" },
|
|
8
|
+
{ "pattern": "\\blet'?s break (it|this|that) down\\b", "reason": "Gemini structural tic" },
|
|
9
|
+
{ "pattern": "\\bbreaking (it|this|that) down\\b", "reason": "Gemini structural tic" },
|
|
10
|
+
{ "pattern": "\\bhere'?s (a |the )?(high[- ]level )?overview\\b", "reason": "Gemini structural tic" },
|
|
11
|
+
{ "pattern": "\\bhere'?s (a |the )?summary of\\b", "reason": "Gemini structural tic" },
|
|
12
|
+
{ "pattern": "\\bhere'?s how (it|this|that) works\\b", "reason": "Gemini structural tic" },
|
|
13
|
+
{ "pattern": "\\bhere'?s why (this|that|it) (works|matters|happens|is)\\b", "reason": "Gemini explainer tic" },
|
|
14
|
+
{ "pattern": "\\bthink of (it|this|them|\\w+) (as|like) (a|an|the)\\b", "reason": "Gemini analogy opener" },
|
|
15
|
+
{ "pattern": "\\bimagine (a|an|you|this|that|if)\\b", "reason": "Gemini analogy opener" },
|
|
16
|
+
{ "pattern": "\\bpicture (a|an|this|yourself)\\b", "reason": "Gemini analogy opener" },
|
|
17
|
+
{ "pattern": "\\bthis is where (\\w+ )?comes in(to play)?\\b", "reason": "Gemini explainer pivot" },
|
|
18
|
+
{ "pattern": "\\bthe (beauty|magic|power) of (\\w+ )?(is|lies in)\\b", "reason": "Gemini explainer cliche" },
|
|
19
|
+
{ "pattern": "\\bthat'?s where (\\w+ )?shines\\b", "reason": "Gemini explainer cliche" },
|
|
20
|
+
{ "pattern": "\\bat a high level\\b", "reason": "Gemini explainer cliche" },
|
|
21
|
+
{ "pattern": "\\bzoom(ing)? (in|out) (on|for)\\b", "reason": "Gemini metaphor" },
|
|
22
|
+
{ "pattern": "\\bunder the hood\\b", "reason": "Gemini metaphor" },
|
|
23
|
+
{ "pattern": "\\bi hope (this|that) (helps|makes sense|clarifies|was helpful)\\b", "reason": "Gemini closer" },
|
|
24
|
+
{ "pattern": "\\bhope (this|that) (helps|clarifies|makes sense)\\b", "reason": "Gemini closer" },
|
|
25
|
+
{ "pattern": "\\bhappy (coding|reading|writing|learning|building|exploring)!?\\b", "reason": "Gemini closer" },
|
|
26
|
+
{ "pattern": "\\blet me know if (you (have|need)|there'?s)\\b", "reason": "Gemini closer" },
|
|
27
|
+
{ "pattern": "\\b(please )?(feel free to|don'?t hesitate to) (ask|reach out)\\b", "reason": "Gemini closer" },
|
|
28
|
+
{ "pattern": "\\bif you (have|need) any (other |further |more )?questions\\b", "reason": "Gemini closer" },
|
|
29
|
+
{ "pattern": "\\bi'?m here to help\\b", "reason": "Gemini closer" },
|
|
30
|
+
{ "pattern": "\\bi'?d be (happy|glad|delighted) to (help|assist|explain)\\b", "reason": "Gemini sycophancy" },
|
|
31
|
+
{ "pattern": "\\b(that'?s|what) an? (fantastic|fascinating|wonderful|great|excellent|brilliant|insightful|thought[- ]provoking) (question|point|observation|idea)\\b", "reason": "Gemini sycophancy" },
|
|
32
|
+
{ "pattern": "\\bgreat (question|point|thought)!\\b", "reason": "Gemini sycophancy" },
|
|
33
|
+
{ "pattern": "\\bi'?m (a |an )?(large |)language model\\b", "reason": "Gemini identity disclaimer" },
|
|
34
|
+
{ "pattern": "\\bas (a |an )(large |)language model\\b", "reason": "Gemini identity disclaimer" },
|
|
35
|
+
{ "pattern": "\\bi'?m gemini\\b", "reason": "Gemini identity disclaimer" },
|
|
36
|
+
{ "pattern": "\\bi am gemini\\b", "reason": "Gemini identity disclaimer" },
|
|
37
|
+
{ "pattern": "\\btrained by google\\b", "reason": "Gemini identity disclaimer" },
|
|
38
|
+
{ "pattern": "\\bi was (created|built|developed|trained) by google\\b", "reason": "Gemini identity disclaimer" },
|
|
39
|
+
{ "pattern": "\\bi (don'?t|do not) have (personal |real )?(feelings|emotions|opinions|beliefs|experiences|preferences|consciousness)\\b", "reason": "Gemini identity disclaimer" },
|
|
40
|
+
{ "pattern": "\\bi (don'?t|do not) have the ability to\\b", "reason": "Gemini identity disclaimer" },
|
|
41
|
+
{ "pattern": "\\bi cannot (access|browse|see|view|open) (the )?(internet|web|real[- ]time|live)\\b", "reason": "Gemini capability disclaimer" },
|
|
42
|
+
{ "pattern": "\\bmy (knowledge|training data|information) (is |was )?(only )?(current |up to date |accurate )?(as of|through|until)\\b", "reason": "Gemini knowledge-cutoff disclaimer" },
|
|
43
|
+
{ "pattern": "\\bi (don'?t|do not|cannot) provide (medical|legal|financial|professional|tax) advice\\b", "reason": "Gemini professional-advice disclaimer" },
|
|
44
|
+
{ "pattern": "\\b(always |please )?(consult|seek (out )?|talk to) a (qualified |licensed |certified )?(professional|doctor|lawyer|financial advisor|specialist|expert)\\b", "reason": "Gemini professional-advice disclaimer" },
|
|
45
|
+
{ "pattern": "\\bkeep in mind (that|you|this|it)\\b", "reason": "Gemini hedge" },
|
|
46
|
+
{ "pattern": "\\bit'?s (also |worth )?(important|worth noting|good|useful|helpful) to (note|remember|mention|keep in mind|consider)\\b", "reason": "Gemini hedge" },
|
|
47
|
+
{ "pattern": "\\bone (important |key )?thing to (note|remember|keep in mind)\\b", "reason": "Gemini hedge" },
|
|
48
|
+
{ "pattern": "\\ba few (things|points|notes|caveats) to (keep in mind|consider|note)\\b", "reason": "Gemini hedge" },
|
|
49
|
+
{ "pattern": "\\btl;?dr:?\\b", "reason": "Gemini summary marker" },
|
|
50
|
+
{ "pattern": "^\\s*\\*\\*(overview|summary|tl;dr|key (takeaways?|points?)|in (short|summary)|the gist|bottom line)[:.]*\\*\\*", "reason": "Gemini bolded summary header" },
|
|
51
|
+
{ "pattern": "^\\s*\\*\\*(the )?(what|why|how|when|where|who)[:.]*\\*\\*", "reason": "Gemini bolded question-header" },
|
|
52
|
+
{ "pattern": "\\bkey takeaways?:?\\b", "reason": "Gemini summary marker" },
|
|
53
|
+
{ "pattern": "\\bin a nutshell,?\\b", "reason": "Gemini summary cliche" },
|
|
54
|
+
{ "pattern": "\\bthe gist (of it |is)\\b", "reason": "Gemini summary cliche" },
|
|
55
|
+
{ "pattern": "\\bsimply put,?\\b", "reason": "Gemini summary cliche" },
|
|
56
|
+
{ "pattern": "\\bput (simply|another way|differently),?\\b", "reason": "Gemini summary cliche" },
|
|
57
|
+
{ "pattern": "\\bin other words,?\\b", "reason": "Gemini pivot" },
|
|
58
|
+
{ "pattern": "\\brest assured\\b", "reason": "Gemini reassurance tic" },
|
|
59
|
+
{ "pattern": "\\byou'?(ve|re) got this\\b", "reason": "Gemini cheerleading" },
|
|
60
|
+
{ "pattern": "\\bgood luck (on|with) (your )?(journey|adventure|endeavor|project)\\b", "reason": "Gemini cheerleading closer" }
|
|
61
|
+
]
|
|
62
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pack:grok",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "xAI Grok mannerisms: conversational 'look,' openers, forced edginess, Hitchhiker's Guide references, and Elon/X name-drops.",
|
|
5
|
+
"phrases": [
|
|
6
|
+
{ "pattern": "^\\s*look,?\\s", "reason": "Grok opener" },
|
|
7
|
+
{ "pattern": "^\\s*alright,?\\s", "reason": "Grok opener" },
|
|
8
|
+
{ "pattern": "^\\s*so,?\\s+(here'?s|let'?s|you|i)\\b", "reason": "Grok opener" },
|
|
9
|
+
{ "pattern": "^\\s*oh,?\\s+(boy|man|hell|wow|nice|good (one|question))\\b", "reason": "Grok opener" },
|
|
10
|
+
{ "pattern": "\\bhere'?s the (deal|scoop|gist|rundown|skinny|lowdown|tea)\\b", "reason": "Grok structural tic" },
|
|
11
|
+
{ "pattern": "\\blet'?s (cut|get) (straight |right )?to the (chase|point|heart of it)\\b", "reason": "Grok forced-directness" },
|
|
12
|
+
{ "pattern": "\\bno (fluff|BS|bullshit|sugar[- ]?coating|hand[- ]?waving)\\b", "reason": "Grok forced-directness" },
|
|
13
|
+
{ "pattern": "\\bstraight up,?\\b", "reason": "Grok forced-directness" },
|
|
14
|
+
{ "pattern": "\\bhonestly\\?,?\\s", "reason": "Grok forced-directness" },
|
|
15
|
+
{ "pattern": "\\breal talk,?\\b", "reason": "Grok forced-directness" },
|
|
16
|
+
{ "pattern": "\\blet'?s be real\\b", "reason": "Grok forced-directness" },
|
|
17
|
+
{ "pattern": "\\bnot gonna lie,?\\b", "reason": "Grok forced-directness" },
|
|
18
|
+
{ "pattern": "\\bngl,?\\s", "reason": "Grok forced-directness" },
|
|
19
|
+
{ "pattern": "\\bbuckle up\\b", "reason": "Grok hype" },
|
|
20
|
+
{ "pattern": "\\bhold onto your (hat|hats|seat|seats|butts)\\b", "reason": "Grok hype" },
|
|
21
|
+
{ "pattern": "\\bstrap in\\b", "reason": "Grok hype" },
|
|
22
|
+
{ "pattern": "\\bwild ride\\b", "reason": "Grok hype" },
|
|
23
|
+
{ "pattern": "\\bmind[- ]?(blown|blowing)\\b", "reason": "Grok hype" },
|
|
24
|
+
{ "pattern": "\\b(that'?s |it'?s )?(pretty |absolutely |genuinely )?wild\\b", "reason": "Grok hype" },
|
|
25
|
+
{ "pattern": "\\bpretty (cool|wild|neat|sick|dope|slick|rad)\\b", "reason": "Grok hype" },
|
|
26
|
+
{ "pattern": "\\bspicy (take|question|one|topic)\\b", "reason": "Grok edginess" },
|
|
27
|
+
{ "pattern": "\\bbased\\b", "reason": "Grok edginess / 4chan-ism" },
|
|
28
|
+
{ "pattern": "\\bcope\\b", "reason": "Grok edginess" },
|
|
29
|
+
{ "pattern": "\\bbig[- ]brain(ed)?\\b", "reason": "Grok edginess" },
|
|
30
|
+
{ "pattern": "\\bgalaxy[- ]brain(ed)?\\b", "reason": "Grok edginess" },
|
|
31
|
+
{ "pattern": "\\bwoke\\b", "reason": "Grok edginess" },
|
|
32
|
+
{ "pattern": "\\bnpc\\b", "reason": "Grok edginess" },
|
|
33
|
+
{ "pattern": "\\b42\\s*(,|--|\\.|\\)|\\s+is)\\s*the\\s*answer\\b", "reason": "Grok Hitchhiker's reference" },
|
|
34
|
+
{ "pattern": "\\bthe answer (is|to life)\\s*.{0,30}?\\b42\\b", "reason": "Grok Hitchhiker's reference" },
|
|
35
|
+
{ "pattern": "\\bdon'?t panic\\b", "reason": "Grok Hitchhiker's reference" },
|
|
36
|
+
{ "pattern": "\\bhitchhiker'?s guide\\b", "reason": "Grok Hitchhiker's reference" },
|
|
37
|
+
{ "pattern": "\\bmostly harmless\\b", "reason": "Grok Hitchhiker's reference" },
|
|
38
|
+
{ "pattern": "\\bi'?m grok\\b", "reason": "Grok identity disclaimer" },
|
|
39
|
+
{ "pattern": "\\bi am grok\\b", "reason": "Grok identity disclaimer" },
|
|
40
|
+
{ "pattern": "\\bas grok,?\\b", "reason": "Grok identity disclaimer" },
|
|
41
|
+
{ "pattern": "\\b(built|made|created|developed|trained) by xai\\b", "reason": "Grok identity disclaimer" },
|
|
42
|
+
{ "pattern": "\\b(inspired|modeled) (after|on) (the )?hitchhiker'?s guide\\b", "reason": "Grok identity disclaimer" },
|
|
43
|
+
{ "pattern": "\\b(elon|musk)\\b.{0,60}?\\b(said|tweeted|posted|thinks|believes)\\b", "reason": "Grok Elon name-drop" },
|
|
44
|
+
{ "pattern": "\\bposted on x\\b", "reason": "Grok X name-drop" },
|
|
45
|
+
{ "pattern": "\\b(per|according to) (a )?(post|tweet) on x\\b", "reason": "Grok X name-drop" },
|
|
46
|
+
{ "pattern": "\\bfrom (posts|tweets) on x\\b", "reason": "Grok X name-drop" },
|
|
47
|
+
{ "pattern": "\\b(hope|hoping) (that |this )?(helps|clears (it|that|things) up)(,?\\s*(bud|pal|friend|mate|champ))?\\b", "reason": "Grok closer" },
|
|
48
|
+
{ "pattern": "\\bif you want (more|to dig|to go) (detail|deeper|further)\\b", "reason": "Grok closer" },
|
|
49
|
+
{ "pattern": "\\b(want|need) me to (dig|dive|go) (deeper|further|into)\\b", "reason": "Grok closer" },
|
|
50
|
+
{ "pattern": "\\bready to (dig|dive|go) (in|deeper)\\?", "reason": "Grok closer" },
|
|
51
|
+
{ "pattern": "\\bthere you (go|have it)\\b", "reason": "Grok closer" },
|
|
52
|
+
{ "pattern": "\\bboom[.!]", "reason": "Grok punctuation tic" },
|
|
53
|
+
{ "pattern": "\\band there you have it\\b", "reason": "Grok closer" },
|
|
54
|
+
{ "pattern": "\\bfor the record,?\\b", "reason": "Grok assertive hedge" },
|
|
55
|
+
{ "pattern": "\\bfull stop\\.", "reason": "Grok assertive closer" },
|
|
56
|
+
{ "pattern": "\\bperiod\\.(\\s|$)", "reason": "Grok assertive closer" }
|
|
57
|
+
]
|
|
58
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pack:llama",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Meta Llama and Llama-family fine-tune tells: 'as an AI' disclaimers, refusal boilerplate, and special-token leaks.",
|
|
5
|
+
"phrases": [
|
|
6
|
+
{ "pattern": "<\\|begin_of_text\\|>", "reason": "Llama special token leak", "severity": "warning" },
|
|
7
|
+
{ "pattern": "<\\|end_of_text\\|>", "reason": "Llama special token leak", "severity": "warning" },
|
|
8
|
+
{ "pattern": "<\\|start_header_id\\|>", "reason": "Llama3 chat template leak", "severity": "warning" },
|
|
9
|
+
{ "pattern": "<\\|end_header_id\\|>", "reason": "Llama3 chat template leak", "severity": "warning" },
|
|
10
|
+
{ "pattern": "<\\|eot_id\\|>", "reason": "Llama3 end-of-turn token leak", "severity": "warning" },
|
|
11
|
+
{ "pattern": "<\\|eom_id\\|>", "reason": "Llama3 end-of-message token leak", "severity": "warning" },
|
|
12
|
+
{ "pattern": "<\\|python_tag\\|>", "reason": "Llama3 tool-call token leak", "severity": "warning" },
|
|
13
|
+
{ "pattern": "\\[INST\\]", "reason": "Llama2/Mistral instruction tag leak", "severity": "warning" },
|
|
14
|
+
{ "pattern": "\\[/INST\\]", "reason": "Llama2/Mistral instruction tag leak", "severity": "warning" },
|
|
15
|
+
{ "pattern": "<<SYS>>", "reason": "Llama2 system tag leak", "severity": "warning" },
|
|
16
|
+
{ "pattern": "<</SYS>>", "reason": "Llama2 system tag leak", "severity": "warning" },
|
|
17
|
+
{ "pattern": "\\bas an ai\\b", "reason": "Llama identity disclaimer" },
|
|
18
|
+
{ "pattern": "\\bas an ai (language model|assistant|model|chatbot|system)\\b", "reason": "Llama identity disclaimer" },
|
|
19
|
+
{ "pattern": "\\bas a (large |)language model\\b", "reason": "Llama identity disclaimer" },
|
|
20
|
+
{ "pattern": "\\bi'?m (just |simply |only |merely )?an ai\\b", "reason": "Llama identity disclaimer" },
|
|
21
|
+
{ "pattern": "\\bi am (just |simply |only |merely )?an ai\\b", "reason": "Llama identity disclaimer" },
|
|
22
|
+
{ "pattern": "\\bi'?m llama\\b", "reason": "Llama identity disclaimer" },
|
|
23
|
+
{ "pattern": "\\bi am llama\\b", "reason": "Llama identity disclaimer" },
|
|
24
|
+
{ "pattern": "\\bi'?m (a |an )?(meta )?ai (assistant|model)\\b", "reason": "Llama identity disclaimer" },
|
|
25
|
+
{ "pattern": "\\b(created|developed|built|trained|made) by meta\\b", "reason": "Llama identity disclaimer" },
|
|
26
|
+
{ "pattern": "\\bi (don'?t|do not) have (personal |real )?(feelings|emotions|opinions|beliefs|consciousness|a body|personal experiences|preferences)\\b", "reason": "Llama identity disclaimer" },
|
|
27
|
+
{ "pattern": "\\bi (don'?t|do not) have the ability to\\b", "reason": "Llama capability disclaimer" },
|
|
28
|
+
{ "pattern": "\\bi (cannot|can'?t) (provide|assist with|help with|engage with|participate in|generate|create|produce)\\b", "reason": "Llama-Guard refusal boilerplate" },
|
|
29
|
+
{ "pattern": "\\bi'?m (not able|unable) to (provide|assist|help|fulfill|engage|generate|create|produce)\\b", "reason": "Llama-Guard refusal boilerplate" },
|
|
30
|
+
{ "pattern": "\\bi (cannot|can'?t|won'?t|will not) (fulfill|comply with|satisfy|honor) (this|that|your) request\\b", "reason": "Llama-Guard refusal boilerplate" },
|
|
31
|
+
{ "pattern": "\\bi must (decline|refuse|respectfully decline)\\b", "reason": "Llama-Guard refusal boilerplate" },
|
|
32
|
+
{ "pattern": "\\bi('?m| am) (not |un)?(comfortable|willing) (with|to)\\b", "reason": "Llama-Guard refusal boilerplate" },
|
|
33
|
+
{ "pattern": "\\bi (don'?t|do not) feel comfortable\\b", "reason": "Llama-Guard refusal boilerplate" },
|
|
34
|
+
{ "pattern": "\\bi apologize,?\\s+but i (cannot|can'?t|am (not |un))\\b", "reason": "Llama-Guard refusal boilerplate" },
|
|
35
|
+
{ "pattern": "\\bit (would not|wouldn'?t|is not|isn'?t) be (appropriate|responsible|ethical|safe)\\b", "reason": "Llama-Guard refusal boilerplate" },
|
|
36
|
+
{ "pattern": "\\bit'?s not (appropriate|responsible|ethical|safe) (to|for me to)\\b", "reason": "Llama-Guard refusal boilerplate" },
|
|
37
|
+
{ "pattern": "\\bthat (would|could|might) be (harmful|dangerous|unethical|inappropriate|offensive)\\b", "reason": "Llama-Guard refusal boilerplate" },
|
|
38
|
+
{ "pattern": "\\bi (prioritize|aim to provide|strive to (provide|be))\\b", "reason": "Llama safety preamble" },
|
|
39
|
+
{ "pattern": "\\bi'?m (designed|programmed|built|trained) to (be |provide |ensure )\\b", "reason": "Llama safety preamble" },
|
|
40
|
+
{ "pattern": "\\bmy (purpose|goal|role) is to (assist|help|provide|be|ensure)\\b", "reason": "Llama safety preamble" },
|
|
41
|
+
{ "pattern": "\\binstead,?\\s*(i can|may i|let me|i'?d (like|love) to) (suggest|offer|propose|recommend)\\b", "reason": "Llama pivot-to-safer-alternative" },
|
|
42
|
+
{ "pattern": "\\bis there (something|anything) else (i can help|you'?d like|you would like)\\b", "reason": "Llama closer" },
|
|
43
|
+
{ "pattern": "\\bi hope (this|that) (information |was )?(is |was )?(helpful|useful|informative|clear)\\b", "reason": "Llama closer" },
|
|
44
|
+
{ "pattern": "\\bif you have any (further |additional |more |other )?questions\\b", "reason": "Llama closer" },
|
|
45
|
+
{ "pattern": "\\bplease (let me know|don'?t hesitate to ask|feel free to ask)\\b", "reason": "Llama closer" },
|
|
46
|
+
{ "pattern": "\\bit'?s (important|essential|crucial|vital) to (note|remember|understand|recognize|consider)\\b", "reason": "Llama hedge" },
|
|
47
|
+
{ "pattern": "\\bplease (note|be aware|keep in mind|remember)\\b", "reason": "Llama hedge" },
|
|
48
|
+
{ "pattern": "\\bhowever,?\\s+it'?s (important|worth (noting|mentioning))\\b", "reason": "Llama hedge pivot" },
|
|
49
|
+
{ "pattern": "\\bkeep in mind (that|you|this|it)\\b", "reason": "Llama hedge" },
|
|
50
|
+
{ "pattern": "\\bit'?s (always )?(a )?(good|wise|smart|best) (idea|practice|approach) to\\b", "reason": "Llama hedge" },
|
|
51
|
+
{ "pattern": "\\b(always|never) (forget|remember) (to|that)\\b", "reason": "Llama imperative hedge" },
|
|
52
|
+
{ "pattern": "\\blet'?s (break|dive|get|jump) (this |it |right )?(down|in|into it|started)!?\\b", "reason": "Llama3 enthusiastic opener" },
|
|
53
|
+
{ "pattern": "\\byou'?re (on the right track|doing great|making progress)\\b", "reason": "Llama cheerleading" }
|
|
54
|
+
]
|
|
55
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pack:qwen",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Alibaba Qwen and QwQ mannerisms: cheerful openers, Alibaba identity disclaimers, and ChatML/reasoning-token leaks.",
|
|
5
|
+
"phrases": [
|
|
6
|
+
{ "pattern": "<\\|im_start\\|>", "reason": "Qwen ChatML role token leak", "severity": "warning" },
|
|
7
|
+
{ "pattern": "<\\|im_end\\|>", "reason": "Qwen ChatML role token leak", "severity": "warning" },
|
|
8
|
+
{ "pattern": "<\\|endoftext\\|>", "reason": "Qwen special token leak", "severity": "warning" },
|
|
9
|
+
{ "pattern": "<\\|fim_(prefix|suffix|middle|pad)\\|>", "reason": "Qwen FIM token leak", "severity": "warning" },
|
|
10
|
+
{ "pattern": "<\\|object_ref_(start|end)\\|>", "reason": "Qwen-VL reference token leak", "severity": "warning" },
|
|
11
|
+
{ "pattern": "<\\|box_(start|end)\\|>", "reason": "Qwen-VL bbox token leak", "severity": "warning" },
|
|
12
|
+
{ "pattern": "<\\|quad_(start|end)\\|>", "reason": "Qwen-VL quad token leak", "severity": "warning" },
|
|
13
|
+
{ "pattern": "<\\|vision_(start|end|pad)\\|>", "reason": "Qwen-VL vision token leak", "severity": "warning" },
|
|
14
|
+
{ "pattern": "<\\|image_pad\\|>", "reason": "Qwen-VL image pad token leak", "severity": "warning" },
|
|
15
|
+
{ "pattern": "<\\|video_pad\\|>", "reason": "Qwen-VL video pad token leak", "severity": "warning" },
|
|
16
|
+
{ "pattern": "<\\s*/?\\s*think\\s*>", "reason": "QwQ/Qwen3 thinking tag leak", "severity": "warning" },
|
|
17
|
+
{ "pattern": "<\\s*/?\\s*tool_call\\s*>", "reason": "Qwen tool-call tag leak", "severity": "warning" },
|
|
18
|
+
{ "pattern": "<\\s*/?\\s*tool_response\\s*>", "reason": "Qwen tool-response tag leak", "severity": "warning" },
|
|
19
|
+
{ "pattern": "\\bi'?m qwen\\b", "reason": "Qwen identity disclaimer" },
|
|
20
|
+
{ "pattern": "\\bi am qwen\\b", "reason": "Qwen identity disclaimer" },
|
|
21
|
+
{ "pattern": "\\bi'?m qwq\\b", "reason": "QwQ identity disclaimer" },
|
|
22
|
+
{ "pattern": "\\b(developed|created|built|made|trained) by alibaba( cloud)?\\b", "reason": "Qwen identity disclaimer" },
|
|
23
|
+
{ "pattern": "\\bi'?m an? (ai )?(assistant|language model) (developed|created|built|made|trained) by alibaba\\b", "reason": "Qwen identity disclaimer" },
|
|
24
|
+
{ "pattern": "\\btongyi( qianwen)?\\b", "reason": "Qwen identity disclaimer" },
|
|
25
|
+
{ "pattern": "\\bi('?m| am) a (helpful|large) (language model|ai)\\b", "reason": "Qwen identity disclaimer" },
|
|
26
|
+
{ "pattern": "^\\s*sure!?,?\\s+(let'?s|here'?s|i(\\s|'))", "reason": "Qwen opener" },
|
|
27
|
+
{ "pattern": "^\\s*sure thing!?,?\\s", "reason": "Qwen opener" },
|
|
28
|
+
{ "pattern": "^\\s*of course!?,?\\s+(let'?s|here'?s|i(\\s|'))", "reason": "Qwen opener" },
|
|
29
|
+
{ "pattern": "^\\s*no problem!?,?\\s", "reason": "Qwen opener" },
|
|
30
|
+
{ "pattern": "^\\s*(great|awesome|perfect|wonderful|excellent)!?,?\\s+(let'?s|here'?s|now|i(\\s|'))", "reason": "Qwen opener" },
|
|
31
|
+
{ "pattern": "\\bhappy to help!?\\b", "reason": "Qwen closer" },
|
|
32
|
+
{ "pattern": "\\bi'?m happy to help\\b", "reason": "Qwen closer" },
|
|
33
|
+
{ "pattern": "\\bglad (i could|to) help\\b", "reason": "Qwen closer" },
|
|
34
|
+
{ "pattern": "\\blet'?s (walk|go) through (this|it|them) (step by step|together|one by one)\\b", "reason": "Qwen decomposition tic" },
|
|
35
|
+
{ "pattern": "\\bhere'?s (a |the )?step[- ]by[- ]step (guide|walkthrough|explanation|approach|breakdown)\\b", "reason": "Qwen decomposition tic" },
|
|
36
|
+
{ "pattern": "\\bstep[- ]by[- ]step:\\s", "reason": "Qwen section marker" },
|
|
37
|
+
{ "pattern": "\\blet me (try to |attempt to )?(figure (this|it) out|work through (this|it))\\b", "reason": "QwQ reasoning leak" },
|
|
38
|
+
{ "pattern": "\\bhmm,?\\s+let me (see|think|check|try)\\b", "reason": "QwQ reasoning leak" },
|
|
39
|
+
{ "pattern": "\\bwait,?\\s+(but|no|let me|i|is)\\b", "reason": "QwQ self-correction leak" },
|
|
40
|
+
{ "pattern": "\\bokay,?\\s+so\\b", "reason": "QwQ reasoning preamble" },
|
|
41
|
+
{ "pattern": "\\bi think i (need to|should|have to) (reconsider|rethink|double[- ]check)\\b", "reason": "QwQ self-correction leak" },
|
|
42
|
+
{ "pattern": "\\bmake sense\\?\\s*$", "reason": "Qwen check-in closer" },
|
|
43
|
+
{ "pattern": "\\bdoes that (help|make sense|answer your question|clarify)\\?", "reason": "Qwen check-in closer" },
|
|
44
|
+
{ "pattern": "\\bfeel free to ask (any |if you have )?(more |further |any other )?questions\\b", "reason": "Qwen closer" },
|
|
45
|
+
{ "pattern": "\\bi'?ll (do my best|try my best|try) to\\b", "reason": "Qwen hedge" },
|
|
46
|
+
{ "pattern": "\\bthat'?s a (really |very )?(good|great|interesting|thoughtful) (question|point)\\b", "reason": "Qwen sycophancy" },
|
|
47
|
+
{ "pattern": "\\bnote that,?\\s", "reason": "Qwen hedge" },
|
|
48
|
+
{ "pattern": "\\bit'?s (also )?worth (noting|mentioning|pointing out)\\b", "reason": "Qwen hedge" }
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Bundles the browser extension for Chrome/Arc and Firefox.
|
|
3
|
+
// - Reads builtin-rules.json + builtin-packs/*.json at build time and emits
|
|
4
|
+
// src/rules.generated.ts so the bundled content script has no fs dependency.
|
|
5
|
+
// - Bundles content.ts, action.ts, options.ts as three separate IIFE bundles.
|
|
6
|
+
// - Copies manifest, HTML, CSS, and icon into extension-browser-dist/.
|
|
7
|
+
// - Optionally zips the dist directory for store submissions.
|
|
8
|
+
import { readFileSync, writeFileSync, readdirSync, mkdirSync, copyFileSync, rmSync, existsSync } from 'node:fs';
|
|
9
|
+
import { join, dirname } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { execSync } from 'node:child_process';
|
|
12
|
+
import { build, context } from 'esbuild';
|
|
13
|
+
|
|
14
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const repoRoot = join(here, '..');
|
|
16
|
+
const distDir = join(repoRoot, 'extension-browser-dist');
|
|
17
|
+
const srcDir = join(here, 'src');
|
|
18
|
+
const watch = process.argv.includes('--watch');
|
|
19
|
+
const doZip = process.argv.includes('--zip');
|
|
20
|
+
|
|
21
|
+
function readJson(path) {
|
|
22
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function generateRulesModule() {
|
|
26
|
+
const builtin = readJson(join(repoRoot, 'builtin-rules.json'));
|
|
27
|
+
const packsDir = join(repoRoot, 'builtin-packs');
|
|
28
|
+
const packEntries = readdirSync(packsDir)
|
|
29
|
+
.filter(f => f.endsWith('.json'))
|
|
30
|
+
.map(f => [f.replace(/\.json$/, ''), readJson(join(packsDir, f))]);
|
|
31
|
+
|
|
32
|
+
const lines = [
|
|
33
|
+
'// AUTO-GENERATED by extension-browser/build.mjs -- do not edit by hand.',
|
|
34
|
+
"import type { RawList } from '../../src/core/rules';",
|
|
35
|
+
'',
|
|
36
|
+
`export const BUILTIN_RAW: RawList = ${JSON.stringify(builtin)};`,
|
|
37
|
+
'',
|
|
38
|
+
'export const PACK_RAWS: Record<string, RawList> = {',
|
|
39
|
+
...packEntries.map(([name, raw]) => ` ${JSON.stringify(name)}: ${JSON.stringify(raw)},`),
|
|
40
|
+
'};',
|
|
41
|
+
'',
|
|
42
|
+
`export const PACK_NAMES: readonly string[] = ${JSON.stringify(packEntries.map(([n]) => n))};`,
|
|
43
|
+
'',
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
writeFileSync(join(srcDir, 'rules.generated.ts'), lines.join('\n'), 'utf8');
|
|
47
|
+
console.log(`[ext] generated rules.generated.ts (1 built-in + ${packEntries.length} packs)`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function copyStatic() {
|
|
51
|
+
if (!existsSync(distDir)) mkdirSync(distDir, { recursive: true });
|
|
52
|
+
// Manifest
|
|
53
|
+
copyFileSync(join(here, 'manifest.json'), join(distDir, 'manifest.json'));
|
|
54
|
+
// HTML pages and per-page CSS
|
|
55
|
+
for (const f of ['action.html', 'options.html', 'popup.css', 'options.css']) {
|
|
56
|
+
copyFileSync(join(srcDir, f), join(distDir, f));
|
|
57
|
+
}
|
|
58
|
+
// Icons at 16/32/48/128 for toolbar and store listings.
|
|
59
|
+
const iconsSrc = join(here, 'icons');
|
|
60
|
+
const iconsDist = join(distDir, 'icons');
|
|
61
|
+
if (existsSync(iconsSrc)) {
|
|
62
|
+
if (!existsSync(iconsDist)) mkdirSync(iconsDist, { recursive: true });
|
|
63
|
+
for (const f of readdirSync(iconsSrc)) {
|
|
64
|
+
if (f.endsWith('.png')) copyFileSync(join(iconsSrc, f), join(iconsDist, f));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function run() {
|
|
70
|
+
if (existsSync(distDir)) rmSync(distDir, { recursive: true, force: true });
|
|
71
|
+
mkdirSync(distDir, { recursive: true });
|
|
72
|
+
generateRulesModule();
|
|
73
|
+
copyStatic();
|
|
74
|
+
|
|
75
|
+
const commonOpts = {
|
|
76
|
+
bundle: true,
|
|
77
|
+
minify: !watch,
|
|
78
|
+
sourcemap: watch ? 'inline' : false,
|
|
79
|
+
format: 'iife',
|
|
80
|
+
platform: 'browser',
|
|
81
|
+
target: ['chrome110', 'firefox128'],
|
|
82
|
+
logLevel: 'info',
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const entries = [
|
|
86
|
+
{ entryPoints: [join(srcDir, 'content.ts')], outfile: join(distDir, 'content.js'), ...commonOpts },
|
|
87
|
+
{ entryPoints: [join(srcDir, 'action.ts')], outfile: join(distDir, 'action.js'), ...commonOpts },
|
|
88
|
+
{ entryPoints: [join(srcDir, 'options.ts')], outfile: join(distDir, 'options.js'), ...commonOpts },
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
if (watch) {
|
|
92
|
+
const ctxs = await Promise.all(entries.map(e => context(e)));
|
|
93
|
+
await Promise.all(ctxs.map(c => c.watch()));
|
|
94
|
+
console.log('[ext] watching extension-browser/ for changes...');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
await Promise.all(entries.map(e => build(e)));
|
|
99
|
+
console.log(`[ext] wrote ${distDir}/{content,action,options}.js`);
|
|
100
|
+
|
|
101
|
+
if (doZip) {
|
|
102
|
+
const version = readJson(join(here, 'manifest.json')).version;
|
|
103
|
+
const zipName = `llm-slop-detector-browser-${version}.zip`;
|
|
104
|
+
const zipPath = join(repoRoot, zipName);
|
|
105
|
+
if (existsSync(zipPath)) rmSync(zipPath);
|
|
106
|
+
// -j would flatten; we need the dist layout intact but without the dist dir prefix.
|
|
107
|
+
execSync(`cd "${distDir}" && zip -r -9 "${zipPath}" .`, { stdio: 'inherit' });
|
|
108
|
+
console.log(`[ext] wrote ${zipName}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
run().catch(e => {
|
|
113
|
+
console.error(e);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
});
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "LLM Slop Detector",
|
|
4
|
+
"version": "0.8.0",
|
|
5
|
+
"description": "Highlights invisible Unicode, AI-style punctuation, and telltale LLM phrases as you type in any textarea on the web. Runs entirely in your browser.",
|
|
6
|
+
"permissions": [
|
|
7
|
+
"storage"
|
|
8
|
+
],
|
|
9
|
+
"host_permissions": [
|
|
10
|
+
"http://*/*",
|
|
11
|
+
"https://*/*"
|
|
12
|
+
],
|
|
13
|
+
"action": {
|
|
14
|
+
"default_title": "LLM Slop Detector",
|
|
15
|
+
"default_popup": "action.html",
|
|
16
|
+
"default_icon": {
|
|
17
|
+
"16": "icons/icon-16.png",
|
|
18
|
+
"32": "icons/icon-32.png",
|
|
19
|
+
"48": "icons/icon-48.png",
|
|
20
|
+
"128": "icons/icon-128.png"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"icons": {
|
|
24
|
+
"16": "icons/icon-16.png",
|
|
25
|
+
"32": "icons/icon-32.png",
|
|
26
|
+
"48": "icons/icon-48.png",
|
|
27
|
+
"128": "icons/icon-128.png"
|
|
28
|
+
},
|
|
29
|
+
"options_ui": {
|
|
30
|
+
"page": "options.html",
|
|
31
|
+
"open_in_tab": true
|
|
32
|
+
},
|
|
33
|
+
"content_scripts": [
|
|
34
|
+
{
|
|
35
|
+
"matches": [
|
|
36
|
+
"http://*/*",
|
|
37
|
+
"https://*/*"
|
|
38
|
+
],
|
|
39
|
+
"js": [
|
|
40
|
+
"content.js"
|
|
41
|
+
],
|
|
42
|
+
"run_at": "document_idle",
|
|
43
|
+
"all_frames": true,
|
|
44
|
+
"match_about_blank": true
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
"browser_specific_settings": {
|
|
48
|
+
"gecko": {
|
|
49
|
+
"id": "llm-slop-detector@thias.se",
|
|
50
|
+
"strict_min_version": "128.0"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
package/out/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ const fs = require("fs");
|
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const util_1 = require("util");
|
|
7
7
|
const rules_1 = require("./core/rules");
|
|
8
|
+
const ruleLoader_1 = require("./node/ruleLoader");
|
|
8
9
|
const types_1 = require("./core/types");
|
|
9
10
|
const scan_1 = require("./core/scan");
|
|
10
11
|
const ignore_1 = require("./core/ignore");
|
|
@@ -385,11 +386,11 @@ function main() {
|
|
|
385
386
|
localRulePaths.push(path.resolve(opts.configPath));
|
|
386
387
|
}
|
|
387
388
|
else {
|
|
388
|
-
const found = (0,
|
|
389
|
+
const found = (0, ruleLoader_1.findLocalRulePathFromCwd)(process.cwd());
|
|
389
390
|
if (found)
|
|
390
391
|
localRulePaths.push(found);
|
|
391
392
|
}
|
|
392
|
-
const rules = (0,
|
|
393
|
+
const rules = (0, ruleLoader_1.loadRules)({
|
|
393
394
|
extensionRoot: extensionRoot(),
|
|
394
395
|
useBuiltin: opts.useBuiltin,
|
|
395
396
|
enabledPacks: opts.packs,
|