llm-slop-detector 0.10.1 → 0.11.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 CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.11.0](https://github.com/mandakan/llm-slop-detector/compare/llm-slop-detector-v0.10.1...llm-slop-detector-v0.11.0) (2026-09-25)
4
+
5
+
6
+ ### Features
7
+
8
+ * add 2026 phrase tells (structural, per-model refreshes, validation openers) and spacing chars ([#115](https://github.com/mandakan/llm-slop-detector/issues/115)) ([0e4e909](https://github.com/mandakan/llm-slop-detector/commit/0e4e90946cd4f31ef49286476ccef55a59f4d121))
9
+ * add citation-artifact leak tokens for Gemini, Grok, DeepSeek, Perplexity, and more ChatGPT markers ([#113](https://github.com/mandakan/llm-slop-detector/issues/113)) ([3b4c396](https://github.com/mandakan/llm-slop-detector/commit/3b4c3965e4aceee22ef8b651e6e2e593608f60bd))
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * line-anchored phrase rules only matched at the start of the document ([#114](https://github.com/mandakan/llm-slop-detector/issues/114)) ([78e0908](https://github.com/mandakan/llm-slop-detector/commit/78e090888924ad29161c3b8f02c28dd99d458bff))
15
+
3
16
  ## [0.10.1](https://github.com/mandakan/llm-slop-detector/compare/llm-slop-detector-v0.10.0...llm-slop-detector-v0.10.1) (2026-08-20)
4
17
 
5
18
 
package/README.md CHANGED
@@ -52,7 +52,7 @@ Turn this off in the options page if you only want to lint your own writing. The
52
52
 
53
53
  - Flags zero-width, BOM, non-breaking spaces, and other invisible Unicode that hides in text and wrecks diffs
54
54
  - Flags AI-style punctuation: em and en dashes, curly quotes, horizontal ellipsis, angle quotes
55
- - Configurable phrase rules: ~40 built-in core rules plus thirteen opt-in packs (`academic`, `cliches`, `puffery`, `fiction`, `claudeisms`, `structural`, `security`, `openai`, `gemini`, `deepseek`, `llama`, `qwen`, `grok`) totalling 500+ curated regex patterns
55
+ - Configurable phrase rules: ~40 built-in core rules plus fourteen opt-in packs (`academic`, `cliches`, `puffery`, `fiction`, `claudeisms`, `structural`, `security`, `openai`, `gemini`, `deepseek`, `llama`, `qwen`, `grok`, `perplexity`) totalling 500+ curated regex patterns
56
56
  - Markdown-aware: skips fenced and inline code, link URLs, and YAML frontmatter so technical prose doesn't drown in false positives
57
57
  - Inline-ignore comments (`<!-- slop-disable -->`, `<!-- slop-disable-next-line -->`, `<!-- slop-disable-line -->`) for one-off exceptions
58
58
  - Hover over any flagged range for the rule selector plus a ready-to-copy `slop-disable-next-line` snippet
@@ -188,20 +188,21 @@ The core list is deliberately conservative: ~40 phrase rules covering the buzzwo
188
188
 
189
189
  - **`academic`** -- words over-represented in LLM-authored academic writing (`bolster`, `elucidate`, `facilitate`, `showcase`, `noteworthy`, ~90 entries). Severity `hint` so the Problems panel stays usable. Derived from [`berenslab/llm-excess-vocab`](https://github.com/berenslab/llm-excess-vocab) (MIT).
190
190
  - **`cliches`** -- general LLM cliche vocabulary (`captivating`, `pinnacle`, `galvanize`, journey/landscape/symphony metaphors). Derived from [`nanxstats/llm-cliches`](https://github.com/nanxstats/llm-cliches) (MIT).
191
- - **`puffery`** -- promotional / editorializing puffery: `rich cultural heritage`, `nestled in the heart of`, `boasts`, `enduring legacy`, `leaves an indelible mark`, `deeply rooted`, `diverse array of`, copula-avoidance flourishes. Original content.
191
+ - **`puffery`** -- promotional / editorializing puffery: `rich cultural heritage`, `nestled in the heart of`, `boasts`, `enduring legacy`, `leaves an indelible mark`, `deeply rooted`, `quiet confidence` / `quietly revolutionary`, `diverse array of`, copula-avoidance flourishes. Original content.
192
192
  - **`fiction`** -- fiction and creative-writing tells (breath hitched, heart hammering, shivers down spine, chestnut eyes, LLM-cliche character names). **Includes adult-fiction markers.** Derived from [`SicariusSicariiStuff/SLOP_Detector`](https://github.com/SicariusSicariiStuff/SLOP_Detector) (Apache-2.0).
193
- - **`claudeisms`** -- Claude-specific mannerisms: sycophantic openers, consent-theater phrasing, "important to note that" hedges. Derived from SLOP_Detector.
194
- - **`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.
195
- - **`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), plus invisible math operators (U+2061-U+2064), deprecated format controls (U+206A-U+206F), the combining grapheme joiner (U+034F), Mongolian free variation selectors (U+180B-U+180D), and the Braille blank (U+2800). Severity `error` (`warning` for the few code points with rare legitimate use). 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.
193
+ - **`claudeisms`** -- Claude-specific mannerisms: sycophantic openers, consent-theater phrasing, "important to note that" hedges, plus 2026 Opus-era tics (`less like a X and more like`, `rather than merely`, `the single most important`, `let me be direct`). Derived from SLOP_Detector; the 2026 additions are original.
194
+ - **`structural`** -- structural LLM tells: "not X but Y" negation pivots, "X is not simply Y" corrective framing, copula avoidance (`stands as a`, `serves as a reminder`), sentence-initial `Moreover` / `Furthermore` / `Additionally` cycling, tailing participle clauses (`underscoring its importance`), vague connection (`widely associated with`), sycophantic and `you're not alone` validation openers, "in this section we'll" meta-commentary, "at the end of the day" closers. Original content.
195
+ - **`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), plus invisible math operators (U+2061-U+2064), deprecated format controls (U+206A-U+206F), the combining grapheme joiner (U+034F), Mongolian free variation selectors (U+180B-U+180D), and the Braille blank (U+2800). Severity `error` (`warning` for the few code points with rare legitimate use). 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. Also the remaining Unicode spacing characters (`U+2000`-`U+200A`, `U+205F`, `U+3000`) that paste-cleaners normalise to a plain space.
196
196
 
197
197
  Model-family packs flag mannerisms specific to one vendor's models. Mix and match:
198
198
 
199
- - **`openai`** -- OpenAI GPT / ChatGPT: citation-markup artifact leaks (`oaicite`, `oai_citation`, `contentReference`, `turn0search0`-style web-tool tokens), AI-identity and knowledge-cutoff disclaimers (`as an AI language model`, `knowledge cutoff`), refusal boilerplate (`I cannot fulfill that request`), and stock opener/closer/hedge scaffolding (`Certainly!`, `here's a comprehensive overview`, `I hope this helps`). Original content.
200
- - **`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.
201
- - **`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|>`).
199
+ - **`openai`** -- OpenAI GPT / ChatGPT: citation-markup artifact leaks (`oaicite`, `oai_citation`, `contentReference`, `attributableIndex`, `turn0search0`-style web-tool tokens, the invisible `U+E200`-`U+E203` citation delimiters, `utm_source=chatgpt.com`), AI-identity and knowledge-cutoff disclaimers (`as an AI language model`, `knowledge cutoff`), refusal boilerplate (`I cannot fulfill that request`), and stock opener/closer/hedge scaffolding (`Certainly!`, `here's a comprehensive overview`, `I hope this helps`). Original content.
200
+ - **`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, identity / knowledge-cutoff / professional-advice disclaimers, citation-markup artifact leaks (`[cite: 1]`, `[span_1](start_span)`), and Gemini 3.1 tics (`incredibly`, `this comprehensive guide`).
201
+ - **`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|>`), and search-citation artifacts (`【1†source】`).
202
202
  - **`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|>`).
203
203
  - **`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.
204
- - **`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.
204
+ - **`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`), Elon/X name-drops, and citation-markup artifact leaks (`grok_card`, `grok_render_citation_card_json`).
205
+ - **`perplexity`** -- Perplexity: file-upload and attachment markup that survives copy-paste (`ppl-ai-file-upload`, `attached_file`) and `utm_source=perplexity` reference URLs. Original content.
205
206
 
206
207
  Enable one or more in your settings:
207
208
 
@@ -1,10 +1,14 @@
1
1
  {
2
2
  "name": "pack:claudeisms",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Claude-specific mannerisms: sycophancy, consent-theater phrasing, and over-careful hedges.",
5
5
  "credits": "Derived from SicariusSicariiStuff/SLOP_Detector (Apache License 2.0). See THIRD_PARTY_NOTICES.md.",
6
6
  "license": "Apache-2.0",
7
7
  "phrases": [
8
+ { "pattern": "\\bless like (a|an) [^.!?\\n]{1,30}? and more like\\b", "reason": "Claude Opus 5 reframing tic, 105x human rate (Graphite 2026)" },
9
+ { "pattern": "\\brather than merely\\b", "reason": "Claude Opus 5 contrastive tic, 160x human rate (Graphite 2026)" },
10
+ { "pattern": "\\b(the )?single most (important|powerful|effective|common|useful|significant|valuable|underrated|overlooked|striking)\\b", "reason": "Claude Opus 5 superlative tic (Graphite 2026)" },
11
+ { "pattern": "\\b(I'?ll|let me) be (direct|candid|blunt)( with you)?\\b", "reason": "Claude directness preamble" },
8
12
  { "pattern": "\\byou'?re absolutely right\\b", "reason": "Claude sycophancy" },
9
13
  { "pattern": "\\byou'?re right (that|to)\\b", "reason": "Claude sycophancy" },
10
14
  { "pattern": "\\b(that'?s|what a) (a )?(great|excellent|fantastic|wonderful|brilliant) (question|point|observation|catch)\\b", "reason": "Claude sycophancy" },
@@ -1,8 +1,9 @@
1
1
  {
2
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.",
3
+ "version": "0.2.0",
4
+ "description": "DeepSeek V3/R1 mannerisms, most notably reasoning-trace tokens and thinking-phase tics that leak into finished output, plus search-citation artifacts (lenticular bracket + dagger).",
5
5
  "phrases": [
6
+ { "pattern": "\u3010\\d+\u2020", "reason": "DeepSeek search-citation artifact leak (lenticular bracket + dagger)", "severity": "warning" },
6
7
  { "pattern": "<\\s*/?\\s*think\\s*>", "reason": "DeepSeek R1 reasoning tag leak", "severity": "warning" },
7
8
  { "pattern": "<\\s*/?\\s*thinking\\s*>", "reason": "reasoning tag leak", "severity": "warning" },
8
9
  { "pattern": "<\\s*/?\\s*reasoning\\s*>", "reason": "reasoning tag leak", "severity": "warning" },
@@ -11,12 +12,12 @@
11
12
  { "pattern": "<\\|\\s*(begin|end)_of_solution\\s*\\|>", "reason": "DeepSeek solution delimiter leak", "severity": "warning" },
12
13
  { "pattern": "<\\|fim_(begin|hole|end)\\|>", "reason": "DeepSeek FIM token leak", "severity": "warning" },
13
14
  { "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" },
15
+ { "pattern": "^[ \t]*wait,?\\s", "reason": "R1 reasoning tic leaking into output" },
16
+ { "pattern": "^[ \t]*(but |so |and )?wait,\\s", "reason": "R1 self-correction leak" },
17
+ { "pattern": "^[ \t]*hmm+,?\\s", "reason": "R1 reasoning tic leaking into output" },
18
+ { "pattern": "^[ \t]*(ah|aha|oh),?\\s", "reason": "R1 reasoning-interjection leak" },
19
+ { "pattern": "^[ \t]*okay,?\\s*(so|let'?s|let me|now)\\b", "reason": "R1 reasoning preamble leak" },
20
+ { "pattern": "^[ \t]*alright,?\\s*(so|let'?s|let me|now)\\b", "reason": "R1 reasoning preamble leak" },
20
21
  { "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
22
  { "pattern": "\\blet me re[- ]?(examine|check|verify|think|evaluate|analyze|consider)\\b", "reason": "R1 self-correction leak" },
22
23
  { "pattern": "\\blet me double[- ]check\\b", "reason": "R1 self-correction leak" },
@@ -1,8 +1,13 @@
1
1
  {
2
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.",
3
+ "version": "0.3.0",
4
+ "description": "Google Gemini (and legacy Bard) mannerisms: breakdown-happy structure, enthusiastic openers/closers, identity disclaimers, and citation-markup artifact leaks ([cite: 1], [span_1](start_span)).",
5
5
  "phrases": [
6
+ { "pattern": "\\[cite:\\s*\\d+", "reason": "Gemini citation-markup artifact leak", "severity": "warning" },
7
+ { "pattern": "\\[span_\\d+\\]", "reason": "Gemini citation-markup artifact leak", "severity": "warning" },
8
+ { "pattern": "\\b(start|end)_span\\b", "reason": "Gemini citation-markup artifact leak", "severity": "warning" },
9
+ { "pattern": "\\bincredibly\\b", "reason": "Gemini 3.1 intensifier, 18x human rate (Graphite 2026)" },
10
+ { "pattern": "\\bthis comprehensive guide\\b", "reason": "Gemini 3.1 framing tic (Graphite 2026)" },
6
11
  { "pattern": "\\bhere'?s (a |the |your )?(detailed |quick |brief |simple )?breakdown\\b", "reason": "Gemini structural tic" },
7
12
  { "pattern": "\\bhere'?s (a |the )?(quick |simple |brief )?rundown\\b", "reason": "Gemini structural tic" },
8
13
  { "pattern": "\\blet'?s break (it|this|that) down\\b", "reason": "Gemini structural tic" },
@@ -47,8 +52,8 @@
47
52
  { "pattern": "\\bone (important |key )?thing to (note|remember|keep in mind)\\b", "reason": "Gemini hedge" },
48
53
  { "pattern": "\\ba few (things|points|notes|caveats) to (keep in mind|consider|note)\\b", "reason": "Gemini hedge" },
49
54
  { "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" },
55
+ { "pattern": "^[ \t]*\\*\\*(overview|summary|tl;dr|key (takeaways?|points?)|in (short|summary)|the gist|bottom line)[:.]*\\*\\*", "reason": "Gemini bolded summary header" },
56
+ { "pattern": "^[ \t]*\\*\\*(the )?(what|why|how|when|where|who)[:.]*\\*\\*", "reason": "Gemini bolded question-header" },
52
57
  { "pattern": "\\bkey takeaways?:?\\b", "reason": "Gemini summary marker" },
53
58
  { "pattern": "\\bin a nutshell,?\\b", "reason": "Gemini summary cliche" },
54
59
  { "pattern": "\\bthe gist (of it |is)\\b", "reason": "Gemini summary cliche" },
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pack:grok",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "xAI Grok mannerisms: conversational 'look,' openers, forced edginess, Hitchhiker's Guide references, and Elon/X name-drops.",
5
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" },
6
+ { "pattern": "^[ \t]*look,?\\s", "reason": "Grok opener" },
7
+ { "pattern": "^[ \t]*alright,?\\s", "reason": "Grok opener" },
8
+ { "pattern": "^[ \t]*so,?\\s+(here'?s|let'?s|you|i)\\b", "reason": "Grok opener" },
9
+ { "pattern": "^[ \t]*oh,?\\s+(boy|man|hell|wow|nice|good (one|question))\\b", "reason": "Grok opener" },
10
10
  { "pattern": "\\bhere'?s the (deal|scoop|gist|rundown|skinny|lowdown|tea)\\b", "reason": "Grok structural tic" },
11
11
  { "pattern": "\\blet'?s (cut|get) (straight |right )?to the (chase|point|heart of it)\\b", "reason": "Grok forced-directness" },
12
12
  { "pattern": "\\bno (fluff|BS|bullshit|sugar[- ]?coating|hand[- ]?waving)\\b", "reason": "Grok forced-directness" },
@@ -36,6 +36,7 @@
36
36
  { "pattern": "\\bhitchhiker'?s guide\\b", "reason": "Grok Hitchhiker's reference" },
37
37
  { "pattern": "\\bmostly harmless\\b", "reason": "Grok Hitchhiker's reference" },
38
38
  { "pattern": "\\bgrok_card\\b", "reason": "Grok citation-markup artifact leak", "severity": "warning" },
39
+ { "pattern": "\\bgrok_render_citation_card_json\\b", "reason": "Grok citation-markup artifact leak", "severity": "warning" },
39
40
  { "pattern": "\\bi'?m grok\\b", "reason": "Grok identity disclaimer" },
40
41
  { "pattern": "\\bi am grok\\b", "reason": "Grok identity disclaimer" },
41
42
  { "pattern": "\\bas grok,?\\b", "reason": "Grok identity disclaimer" },
@@ -1,12 +1,21 @@
1
1
  {
2
2
  "name": "pack:openai",
3
- "version": "0.1.0",
4
- "description": "OpenAI GPT / ChatGPT tells: citation-markup artifact leaks (oaicite, contentReference), AI-identity and knowledge-cutoff disclaimers, refusal boilerplate, and stock opener/closer/hedge scaffolding. Original content -- regexes hand-authored to match commonly-observed stock phrasing; no third-party rule set incorporated.",
3
+ "version": "0.3.0",
4
+ "description": "OpenAI GPT / ChatGPT tells: citation-markup artifact leaks (oaicite, contentReference, attributableIndex, the U+E200-U+E203 private-use citation delimiters, utm_source=chatgpt.com), AI-identity and knowledge-cutoff disclaimers, refusal boilerplate, and stock opener/closer/hedge scaffolding. Original content -- regexes hand-authored to match commonly-observed stock phrasing; no third-party rule set incorporated.",
5
+ "chars": [
6
+ { "char": "\ue200", "name": "PRIVATE USE U+E200 (ChatGPT citation delimiter)", "severity": "warning", "replacement": "" },
7
+ { "char": "\ue201", "name": "PRIVATE USE U+E201 (ChatGPT citation delimiter)", "severity": "warning", "replacement": "" },
8
+ { "char": "\ue202", "name": "PRIVATE USE U+E202 (ChatGPT citation delimiter)", "severity": "warning", "replacement": "" },
9
+ { "char": "\ue203", "name": "PRIVATE USE U+E203 (ChatGPT citation delimiter)", "severity": "warning", "replacement": "" }
10
+ ],
5
11
  "phrases": [
6
12
  { "pattern": "\\boaicite\\b", "reason": "ChatGPT citation-markup artifact leak" },
7
13
  { "pattern": "\\boai_citation\\b", "reason": "ChatGPT citation-markup artifact leak" },
8
14
  { "pattern": "\\bcontentReference\\b", "reason": "ChatGPT citation-markup artifact leak", "severity": "warning" },
9
15
  { "pattern": ":contentReference\\[oaicite", "reason": "ChatGPT citation-markup artifact leak", "severity": "warning" },
16
+ { "pattern": "\\battributableIndex\\b", "reason": "ChatGPT citation-markup artifact leak", "severity": "warning" },
17
+ { "pattern": "\\b(cite|filecite|navlist|video)turn\\d+", "reason": "ChatGPT citation token leak (invisible delimiters stripped)", "severity": "warning" },
18
+ { "pattern": "utm_source=(chatgpt\\.com|openai)", "reason": "ChatGPT-generated reference URL tracking parameter", "severity": "warning" },
10
19
  { "pattern": "\\bturn\\d+(search|news|view|image|forecast|finance)\\d+\\b", "reason": "ChatGPT web-tool citation token leak", "severity": "warning" },
11
20
  { "pattern": "\\bas an? (AI|artificial intelligence)(,| developed| created| trained| language model| assistant)", "reason": "GPT identity disclaimer", "severity": "warning" },
12
21
  { "pattern": "\\bas a large language model\\b", "reason": "GPT identity disclaimer", "severity": "warning" },
@@ -16,9 +25,9 @@
16
25
  { "pattern": "\\bI (don'?t|do not) have (access to |the ability to )?(real[- ]time|live|up[- ]to[- ]date|current|personal)\\b", "reason": "GPT capability disclaimer", "severity": "warning" },
17
26
  { "pattern": "\\bI (can'?t|cannot|am unable to|am not able to) (fulfill|comply with|assist with|help with) (that|this) request\\b", "reason": "GPT refusal boilerplate", "severity": "warning" },
18
27
  { "pattern": "\\bI'?m (sorry|afraid),? but (as|I)\\b", "reason": "GPT refusal boilerplate" },
19
- { "pattern": "^\\s*Certainly[!,]", "reason": "GPT opener" },
20
- { "pattern": "^\\s*Sure(?: thing)?[!,]", "reason": "GPT opener" },
21
- { "pattern": "^\\s*Sure,? (here'?s|here is|I can|let me)\\b", "reason": "GPT opener" },
28
+ { "pattern": "^[ \t]*Certainly[!,]", "reason": "GPT opener" },
29
+ { "pattern": "^[ \t]*Sure(?: thing)?[!,]", "reason": "GPT opener" },
30
+ { "pattern": "^[ \t]*Sure,? (here'?s|here is|I can|let me)\\b", "reason": "GPT opener" },
22
31
  { "pattern": "\\bGreat question[!.]", "reason": "GPT opener" },
23
32
  { "pattern": "\\bI'?d be (happy|glad) to (help|assist)\\b", "reason": "GPT opener" },
24
33
  { "pattern": "\\bhere'?s a (comprehensive|detailed|quick|brief|step[- ]by[- ]step) (overview|breakdown|rundown|guide|look)\\b", "reason": "GPT framing" },
@@ -28,6 +37,7 @@
28
37
  { "pattern": "\\b(feel free|don'?t hesitate) to (ask|reach out|let me know)\\b", "reason": "GPT closer" },
29
38
  { "pattern": "\\bis there anything else (I can|you'?d like)\\b", "reason": "GPT closer" },
30
39
  { "pattern": "\\bgenerally speaking,?\\b", "reason": "GPT hedge" },
40
+ { "pattern": "\\brather than relying (on|solely)\\b", "reason": "GPT-6 contrastive tic, 187x human rate (Graphite 2026)" },
31
41
  { "pattern": "\\bit'?s (important|worth|essential) to (note|remember|keep in mind|mention) that\\b", "reason": "GPT hedge" },
32
42
  { "pattern": "\\bdepending on (the|your) (context|specific|particular|needs|situation)\\b", "reason": "GPT hedge" }
33
43
  ]
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "pack:perplexity",
3
+ "version": "0.1.0",
4
+ "description": "Perplexity artifact leaks: file-upload and attachment markup that survives copy-paste out of the answer UI. Original content -- hand-authored regexes for functional strings; no third-party rule set incorporated.",
5
+ "phrases": [
6
+ { "pattern": "\\bppl-ai-file-upload\\b", "reason": "Perplexity file-upload artifact leak", "severity": "warning" },
7
+ { "pattern": "\\battached_file\\b", "reason": "Perplexity attachment artifact leak", "severity": "warning" },
8
+ { "pattern": "utm_source=perplexity", "reason": "Perplexity-generated reference URL tracking parameter", "severity": "warning" }
9
+ ]
10
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pack:puffery",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Promotional / editorializing puffery common in LLM prose: 'rich cultural heritage', 'nestled in the heart of', 'boasts', 'enduring legacy', 'leaves an indelible mark', and copula-avoidance flourishes. Original content -- regexes hand-authored, no third-party rule set incorporated.",
5
5
  "phrases": [
6
6
  { "pattern": "\\brich (cultural|literary|artistic|musical|architectural|natural|historical|spiritual) heritage\\b", "reason": "puffery" },
@@ -14,6 +14,8 @@
14
14
  { "pattern": "\\bdeeply rooted\\b", "reason": "puffery" },
15
15
  { "pattern": "\\bsetting the stage for\\b", "reason": "LLM transition cliche" },
16
16
  { "pattern": "\\b(a |an )?diverse array of\\b", "reason": "puffery" },
17
- { "pattern": "\\bbreathtaking (views?|scenery|beauty|landscapes?|vistas?)\\b", "reason": "puffery" }
17
+ { "pattern": "\\bbreathtaking (views?|scenery|beauty|landscapes?|vistas?)\\b", "reason": "puffery" },
18
+ { "pattern": "\\bquiet (confidence|strength|power|rebellion|revolution|dignity|resilience|brilliance|truth|grief|ache|hum|defiance|intensity|luxury)\\b", "reason": "'quiet X' editorializing flourish (2026 cross-vendor tell)" },
19
+ { "pattern": "\\bquietly (powerful|revolutionary|radical|devastating|transformative|reshaping|redefining|remarkable|profound|brilliant)\\b", "reason": "'quietly X' editorializing flourish (2026 cross-vendor tell)" }
18
20
  ]
19
21
  }
@@ -23,11 +23,11 @@
23
23
  { "pattern": "\\bi'?m an? (ai )?(assistant|language model) (developed|created|built|made|trained) by alibaba\\b", "reason": "Qwen identity disclaimer" },
24
24
  { "pattern": "\\btongyi( qianwen)?\\b", "reason": "Qwen identity disclaimer" },
25
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" },
26
+ { "pattern": "^[ \t]*sure!?,?\\s+(let'?s|here'?s|i(\\s|'))", "reason": "Qwen opener" },
27
+ { "pattern": "^[ \t]*sure thing!?,?\\s", "reason": "Qwen opener" },
28
+ { "pattern": "^[ \t]*of course!?,?\\s+(let'?s|here'?s|i(\\s|'))", "reason": "Qwen opener" },
29
+ { "pattern": "^[ \t]*no problem!?,?\\s", "reason": "Qwen opener" },
30
+ { "pattern": "^[ \t]*(great|awesome|perfect|wonderful|excellent)!?,?\\s+(let'?s|here'?s|now|i(\\s|'))", "reason": "Qwen opener" },
31
31
  { "pattern": "\\bhappy to help!?\\b", "reason": "Qwen closer" },
32
32
  { "pattern": "\\bi'?m happy to help\\b", "reason": "Qwen closer" },
33
33
  { "pattern": "\\bglad (i could|to) help\\b", "reason": "Qwen closer" },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pack:security",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "LLM-weaponized invisibles above the BMP: tag characters (ASCII-smuggler prompt injection), variation selectors (arbitrary-data smuggling), invisible math operators, and deprecated/zero-width format controls used to hide instructions or smuggle data. Severity: error (warning for chars with rare legitimate use).",
5
5
  "chars": [
6
6
  {"char":"󠀁","name":"LANGUAGE TAG (U+E0001)","severity":"error","replacement":""},
@@ -369,6 +369,19 @@
369
369
  {"char":"᠋","name":"MONGOLIAN FREE VARIATION SELECTOR ONE (U+180B)","severity":"warning","replacement":""},
370
370
  {"char":"᠌","name":"MONGOLIAN FREE VARIATION SELECTOR TWO (U+180C)","severity":"warning","replacement":""},
371
371
  {"char":"᠍","name":"MONGOLIAN FREE VARIATION SELECTOR THREE (U+180D)","severity":"warning","replacement":""},
372
- {"char":"⠀","name":"BRAILLE PATTERN BLANK (U+2800)","severity":"warning","replacement":""}
372
+ {"char":"⠀","name":"BRAILLE PATTERN BLANK (U+2800)","severity":"warning","replacement":""},
373
+ {"char":" ","name":"EN QUAD (U+2000)","severity":"warning","replacement":" "},
374
+ {"char":" ","name":"EM QUAD (U+2001)","severity":"warning","replacement":" "},
375
+ {"char":" ","name":"EN SPACE (U+2002)","severity":"warning","replacement":" "},
376
+ {"char":" ","name":"EM SPACE (U+2003)","severity":"warning","replacement":" "},
377
+ {"char":" ","name":"THREE-PER-EM SPACE (U+2004)","severity":"warning","replacement":" "},
378
+ {"char":" ","name":"FOUR-PER-EM SPACE (U+2005)","severity":"warning","replacement":" "},
379
+ {"char":" ","name":"SIX-PER-EM SPACE (U+2006)","severity":"warning","replacement":" "},
380
+ {"char":" ","name":"FIGURE SPACE (U+2007)","severity":"warning","replacement":" "},
381
+ {"char":" ","name":"PUNCTUATION SPACE (U+2008)","severity":"warning","replacement":" "},
382
+ {"char":" ","name":"THIN SPACE (U+2009)","severity":"warning","replacement":" "},
383
+ {"char":" ","name":"HAIR SPACE (U+200A)","severity":"warning","replacement":" "},
384
+ {"char":" ","name":"MEDIUM MATHEMATICAL SPACE (U+205F)","severity":"warning","replacement":" "},
385
+ {"char":" ","name":"IDEOGRAPHIC SPACE (U+3000)","severity":"warning","replacement":" "}
373
386
  ]
374
387
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pack:structural",
3
- "version": "0.1.0",
4
- "description": "Structural LLM tells: 'not X but Y' pivots, sycophantic openers, section-closer cliches, and meta-commentary.",
3
+ "version": "0.2.0",
4
+ "description": "Structural LLM tells: negation pivots, corrective framing, copula avoidance, transition cycling, tailing participle clauses, sycophantic and validation openers, meta-commentary. Original content -- hand-authored regexes; no third-party rule set incorporated.",
5
5
  "phrases": [
6
6
  { "pattern": "\\bnot (just|only|merely|simply) [^.!?\\n]{1,40}?,? but (also )?\\b", "reason": "negation-pivot -- LLM signature construction" },
7
7
  { "pattern": "\\bit(?:'?s| is| was)(?:n'?t| not) (?:just |only |merely )?\\w+[,;]\\s+it(?:'?s| is| was)\\b", "reason": "LLM contrast pivot" },
@@ -23,9 +23,18 @@
23
23
  { "pattern": "\\bthe key (takeaway|point|thing|lesson) (is|to remember|here)\\b", "reason": "LLM summary cliche" },
24
24
  { "pattern": "\\b(at the end of the day|when all is said and done)\\b", "reason": "LLM closer cliche" },
25
25
  { "pattern": "\\bthe bottom line is\\b", "reason": "LLM closer cliche" },
26
- { "pattern": "^\\s*(Great|Excellent|Fantastic|Absolutely|Perfect|Wonderful|Certainly)[!.,]", "reason": "sycophantic opener" },
27
- { "pattern": "^\\s*That'?s an? (excellent|great|fantastic|wonderful|brilliant|insightful)\\b", "reason": "sycophantic opener" },
28
- { "pattern": "^\\s*You raise\\b", "reason": "sycophantic opener" },
29
- { "pattern": "^\\s*I('?d| would) (be happy|love) to\\b", "reason": "sycophantic opener" }
26
+ { "pattern": "^[ \t]*(Great|Excellent|Fantastic|Absolutely|Perfect|Wonderful|Certainly)[!.,]", "reason": "sycophantic opener" },
27
+ { "pattern": "^[ \t]*That'?s an? (excellent|great|fantastic|wonderful|brilliant|insightful)\\b", "reason": "sycophantic opener" },
28
+ { "pattern": "^[ \t]*You raise\\b", "reason": "sycophantic opener" },
29
+ { "pattern": "^[ \t]*I('?d| would) (be happy|love) to\\b", "reason": "sycophantic opener" },
30
+ { "pattern": "\\brather than (simply|just) \\w+ing\\b", "reason": "'Y rather than X' contrastive negation (Wikipedia: Signs of AI writing)" },
31
+ { "pattern": "\\b(is|are|was|were)(?:n'?t| not) (simply|merely) (a|an|about|that)\\b", "reason": "'X is not simply Y' corrective framing (GPT-6 tell, 576x human rate per Graphite 2026)" },
32
+ { "pattern": "\\b(stands|functions|operates) as (a|an|the)\\b", "reason": "copula avoidance: 'stands as' for 'is' (Wikipedia: Signs of AI writing)" },
33
+ { "pattern": "\\bserves as (a|an) (reminder|symbol|beacon|bridge|catalyst|cornerstone|foundation|gateway|hub|pillar|powerful|stark|poignant)\\b", "reason": "copula avoidance: 'serves as a' flourish (Wikipedia: Signs of AI writing)" },
34
+ { "pattern": "^[ \\t]*(Moreover|Furthermore|Additionally),", "reason": "sentence-initial transition cycling (Wikipedia: Signs of AI writing; 'Furthermore' 43x human rate in Gemini 3.1 per Graphite 2026)" },
35
+ { "pattern": "\\b(highlighting|underscoring|emphasizing|emphasising|reflecting|showcasing|signaling|signalling|cementing|solidifying|reinforcing|symbolizing|symbolising) (its|their|his|her|the) (importance|significance|role|commitment|dedication|broader|growing|ongoing|enduring|lasting|continued)\\b", "reason": "tailing participle clause adding vague significance (Wikipedia: Signs of AI writing, July 2026)" },
36
+ { "pattern": "\\b(widely|particularly|closely|often|strongly) associated with\\b", "reason": "vague expression of connection (Wikipedia: Signs of AI writing, August 2026)" },
37
+ { "pattern": "\\bin (connection|association) with\\b", "reason": "vague expression of connection (Wikipedia: Signs of AI writing, August 2026)" },
38
+ { "pattern": "\\byou'?re not (alone|broken|crazy|imagining (it|this|things)|overreacting|wrong (to|for))\\b", "reason": "unsolicited validation opener (2026 cross-vendor tell)" }
30
39
  ]
31
40
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "LLM Slop Detector",
4
- "version": "0.10.1",
4
+ "version": "0.11.0",
5
5
  "description": "Flags invisible Unicode, AI-style punctuation, and telltale LLM phrases as you type -- or in any page you're reading. Local only.",
6
6
  "permissions": [
7
7
  "storage"
package/out/cli.js CHANGED
@@ -1,8 +1,41 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
3
36
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const fs = require("fs");
5
- const path = require("path");
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
6
39
  const util_1 = require("util");
7
40
  const rules_1 = require("./core/rules");
8
41
  const ruleLoader_1 = require("./node/ruleLoader");
@@ -1,11 +1,44 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.SLOPIGNORE_FILENAME = void 0;
4
37
  exports.parseIgnorePatterns = parseIgnorePatterns;
5
38
  exports.buildIgnoreMatcher = buildIgnoreMatcher;
6
39
  exports.loadIgnoreMatcher = loadIgnoreMatcher;
7
- const fs = require("fs");
8
- const path = require("path");
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
9
42
  exports.SLOPIGNORE_FILENAME = '.slopignore';
10
43
  // Parse .gitignore-style pattern lines into matchers. Blank lines and lines
11
44
  // starting with `#` are skipped. `!foo` negates; trailing `/` means
package/out/core/rules.js CHANGED
@@ -4,7 +4,7 @@ exports.BUILTIN_PACKS = exports.LOCAL_RULES_FILENAME = void 0;
4
4
  exports.parseSeverityOverrides = parseSeverityOverrides;
5
5
  exports.loadRules = loadRules;
6
6
  exports.LOCAL_RULES_FILENAME = '.llmsloprc.json';
7
- exports.BUILTIN_PACKS = ['academic', 'cliches', 'puffery', 'fiction', 'claudeisms', 'structural', 'security', 'openai', 'gemini', 'deepseek', 'llama', 'qwen', 'grok'];
7
+ exports.BUILTIN_PACKS = ['academic', 'cliches', 'puffery', 'fiction', 'claudeisms', 'structural', 'security', 'openai', 'gemini', 'deepseek', 'llama', 'qwen', 'grok', 'perplexity'];
8
8
  function parseSeverity(s, fallback) {
9
9
  switch (s) {
10
10
  case 'error': return 'error';
@@ -61,7 +61,7 @@ function ingestList(raw, origin, target) {
61
61
  continue;
62
62
  let regex;
63
63
  try {
64
- regex = new RegExp(p.pattern, 'gi');
64
+ regex = new RegExp(p.pattern, 'gim');
65
65
  }
66
66
  catch (e) {
67
67
  console.warn(`[LLM Slop] Invalid regex in ${origin}: ${p.pattern}`, e);
package/out/extension.js CHANGED
@@ -1,11 +1,44 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.activate = activate;
4
37
  exports.deactivate = deactivate;
5
- const vscode = require("vscode");
6
- const path = require("path");
7
- const fs = require("fs");
8
- const fsp = require("fs/promises");
38
+ const vscode = __importStar(require("vscode"));
39
+ const path = __importStar(require("path"));
40
+ const fs = __importStar(require("fs"));
41
+ const fsp = __importStar(require("fs/promises"));
9
42
  const rules_1 = require("./rules");
10
43
  const scan_1 = require("./core/scan");
11
44
  const comments_1 = require("./core/comments");
package/out/mcp.js CHANGED
@@ -1,8 +1,41 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
3
36
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const fs = require("fs");
5
- const path = require("path");
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
6
39
  const util_1 = require("util");
7
40
  const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
8
41
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
@@ -1,9 +1,42 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.loadRules = loadRules;
4
37
  exports.findLocalRulePathFromCwd = findLocalRulePathFromCwd;
5
- const fs = require("fs");
6
- const path = require("path");
38
+ const fs = __importStar(require("fs"));
39
+ const path = __importStar(require("path"));
7
40
  const rules_1 = require("../core/rules");
8
41
  function readJsonFile(p) {
9
42
  try {
package/out/rules.js CHANGED
@@ -1,11 +1,44 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.BUILTIN_PACKS = exports.LOCAL_RULES_FILENAME = void 0;
4
37
  exports.severityToVscode = severityToVscode;
5
38
  exports.loadRules = loadRules;
6
- const vscode = require("vscode");
7
- const fs = require("fs");
8
- const path = require("path");
39
+ const vscode = __importStar(require("vscode"));
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
9
42
  const rules_1 = require("./core/rules");
10
43
  Object.defineProperty(exports, "BUILTIN_PACKS", { enumerable: true, get: function () { return rules_1.BUILTIN_PACKS; } });
11
44
  Object.defineProperty(exports, "LOCAL_RULES_FILENAME", { enumerable: true, get: function () { return rules_1.LOCAL_RULES_FILENAME; } });
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.10.1",
5
+ "version": "0.11.0",
6
6
  "publisher": "thias-se",
7
7
  "engines": {
8
8
  "vscode": "^1.95.0"
@@ -85,7 +85,8 @@
85
85
  "deepseek",
86
86
  "llama",
87
87
  "qwen",
88
- "grok"
88
+ "grok",
89
+ "perplexity"
89
90
  ],
90
91
  "enumDescriptions": [
91
92
  "Words over-represented in LLM-authored academic writing (derived from berenslab/llm-excess-vocab, MIT)",
@@ -93,14 +94,15 @@
93
94
  "Promotional / editorializing puffery: 'rich cultural heritage', 'nestled in the heart of', 'boasts', 'enduring legacy', 'leaves an indelible mark', copula-avoidance flourishes. Original content.",
94
95
  "Fiction and creative-writing LLM tells, including NSFW markers (derived from SicariusSicariiStuff/SLOP_Detector, Apache-2.0)",
95
96
  "Claude-specific mannerisms and sycophantic consent-theater phrasing (derived from SicariusSicariiStuff/SLOP_Detector, Apache-2.0)",
96
- "Structural LLM tells: 'not X but Y', sycophantic openers, meta-commentary",
97
- "Flags LLM-weaponized invisibles above the BMP: tag chars (ASCII-smuggler prompt injection) and variation selectors (arbitrary-data smuggling), plus invisible math operators and deprecated format controls. Severity: error/warning.",
98
- "OpenAI GPT / ChatGPT tells: citation-markup artifact leaks (oaicite, contentReference), AI-identity and knowledge-cutoff disclaimers, refusal boilerplate, and stock opener/closer/hedge scaffolding",
99
- "Google Gemini / Bard tells: breakdown-happy structure, 'I hope this helps' closers, identity and knowledge-cutoff disclaimers",
97
+ "Structural LLM tells: 'not X but Y', 'X is not simply Y', copula avoidance, Moreover/Furthermore cycling, tailing participle clauses, sycophantic and validation openers, meta-commentary",
98
+ "Flags LLM-weaponized invisibles above the BMP: tag chars (ASCII-smuggler prompt injection) and variation selectors (arbitrary-data smuggling), plus invisible math operators, deprecated format controls, and the remaining Unicode spacing characters. Severity: error/warning.",
99
+ "OpenAI GPT / ChatGPT tells: citation-markup artifact leaks (oaicite, contentReference, invisible U+E200-U+E203 delimiters), AI-identity and knowledge-cutoff disclaimers, refusal boilerplate, and stock opener/closer/hedge scaffolding",
100
+ "Google Gemini / Bard tells: breakdown-happy structure, 'I hope this helps' closers, identity and knowledge-cutoff disclaimers, and [cite: 1] / [span_1] citation artifacts",
100
101
  "DeepSeek V3/R1 tells, notably <think> tags and R1 reasoning tics ('Wait,', 'Hmm,', 'Let me reconsider') leaking into output",
101
102
  "Meta Llama tells: 'as an AI' disclaimers, Llama-Guard refusal boilerplate, and Llama2/Llama3 chat-template token leaks ([INST], <|eot_id|>, etc.)",
102
103
  "Alibaba Qwen / QwQ tells: cheerful 'Sure!' openers, Alibaba identity disclaimers, and ChatML / QwQ reasoning token leaks",
103
- "xAI Grok tells: 'Look,' openers, forced edginess ('based', 'cope', 'no BS'), Hitchhiker's Guide references, and Elon/X name-drops"
104
+ "xAI Grok tells: 'Look,' openers, forced edginess ('based', 'cope', 'no BS'), Hitchhiker's Guide references, Elon/X name-drops, and citation-markup artifact leaks",
105
+ "Perplexity artifact leaks: file-upload / attachment markup (ppl-ai-file-upload, attached_file) and utm_source=perplexity reference URLs"
104
106
  ]
105
107
  },
106
108
  "default": [],
@@ -242,13 +244,13 @@
242
244
  "package:browser": "node extension-browser/build.mjs --zip"
243
245
  },
244
246
  "devDependencies": {
245
- "@types/chrome": "^0.1.40",
246
- "@types/node": "^25.6.0",
247
+ "@types/chrome": "^0.3.0",
248
+ "@types/node": "^26.6.2",
247
249
  "@types/vscode": "^1.95.0",
248
250
  "esbuild": "^0.28.0",
249
- "typescript": "^5.7.0"
251
+ "typescript": "^7.0.2"
250
252
  },
251
253
  "dependencies": {
252
- "@modelcontextprotocol/sdk": "^1.29.0"
254
+ "@modelcontextprotocol/sdk": "^1.30.1"
253
255
  }
254
256
  }