zero-slop 2.8.9 → 2.8.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  <img alt="tests" src="https://img.shields.io/badge/tests-passing-227B5B">
6
6
  <img alt="dependencies" src="https://img.shields.io/badge/runtime%20dependencies-0-227B5B">
7
7
  <img alt="privacy" src="https://img.shields.io/badge/learning-private-227B5B">
8
- <img alt="version" src="https://img.shields.io/badge/version-2.8.9-72528F">
8
+ <img alt="version" src="https://img.shields.io/badge/version-2.8.10-72528F">
9
9
  <a href="https://hol.org/guard/plugins"><img alt="Listed in the HOL plugin registry" src="https://img.shields.io/badge/HOL%20registry-listed-2C6E8F"></a>
10
10
  <a href="https://github.com/hashgraph-online/awesome-ai-plugins#tools--integrations"><img alt="Listed in awesome-ai-plugins" src="https://img.shields.io/badge/awesome--ai--plugins-listed-2C6E8F"></a>
11
11
  <a href="https://zero-slop.ai/try/"><img alt="This README scores 11.6 out of 100 on the slop score, inside the human range" src="https://img.shields.io/badge/slop%20score-11.6%2F100-0f7d55"></a>
@@ -163,7 +163,9 @@ is an engineering choice.
163
163
  Missed editorial targets do not erase a useful edit or start an open-ended loop. Zero
164
164
  Slop permits one targeted repair and one local recheck, then returns the safest
165
165
  source-preserving edit with a plain warning. On the website, a failed model call falls
166
- back to a conservative local edit instead of retrying through more providers.
166
+ back to the same conservative local editor shipped with the installed skill instead of
167
+ retrying through more providers. Shared fixtures require the browser and local command
168
+ to return the same fallback edit, character for character.
167
169
 
168
170
  Studies find
169
171
  [predictable wording](https://arxiv.org/abs/2301.11305) and
package/SKILL.md CHANGED
@@ -2,7 +2,7 @@
2
2
  name: zero-slop
3
3
  license: MIT
4
4
  metadata:
5
- version: "2.8.9"
5
+ version: "2.8.10"
6
6
  author: manavmishra
7
7
  description: Turn drafts into sharp, natural prose or inspect them without rewriting. Zero Slop runs inside the user's existing AI assistant; Claude, GPT, or another compatible model reads and edits in context while local tools point to exact phrases and protect the source. Use when the user asks to humanize or de-slop writing, inspect AI-sounding patterns, fix text that reads like ChatGPT, polish outward-facing prose, draft social or LinkedIn content, or apply a final quality check to prose the agent generated. The workflow preserves facts, voice, and format and learns privately from repeated, reason-labelled human edits.
8
8
  ---
@@ -643,9 +643,13 @@ restart the complete editorial sequence. If the second check still finds a probl
643
643
  return the safest source-preserving edit and name the remaining issue plainly.
644
644
 
645
645
  If an AI editorial role returns no usable text, record that it was unavailable and
646
- continue from the last source-preserving text. Unavailability is an abstention, not a
647
- reason to retry, switch models, or replay earlier roles. A caller with a one-request
648
- budget must never make a second remote request. Report any role that did not complete.
646
+ continue from the last source-preserving text. For an explicit rewrite request, if that
647
+ text is still the unchanged source, run `python3 scripts/rescue.py -` on the source and
648
+ pass its output through the same scorer and fact gate. This deterministic availability
649
+ editor removes only reviewed stock wrappers and never certifies itself; label its use
650
+ plainly. Unavailability is an abstention, not a reason to retry, switch models, or replay
651
+ earlier roles. A caller with a one-request budget must never make a second remote
652
+ request. Report any role that did not complete.
649
653
 
650
654
  A required repair may raise the writing score from the previous draft as long as it
651
655
  stays below the release limit. Source meaning, stated emotion, and factual accuracy
@@ -967,6 +971,9 @@ the AI model already running in the assistant or rewrite this `SKILL.md`.
967
971
  requires the section A counts to be written down rather than judged silently.
968
972
  - `references/evidence.md` — the research basis: papers, detector mechanics,
969
973
  and why each ladder rung is ordered where it is.
974
+ - `scripts/rescue.py` — the conservative, no-network availability editor shared by
975
+ installed skills and the web demo. It returns a changed draft when a known safe edit
976
+ is available, then leaves approval to the scorer and fact gate.
970
977
 
971
978
  ## Worked example (LinkedIn)
972
979
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zero-slop",
3
- "version": "2.8.9",
3
+ "version": "2.8.10",
4
4
  "description": "An Agent Skill that scores AI-sounding prose locally, guides the host AI through an editorial rewrite, and checks changed source details. The local tools run offline with zero dependencies.",
5
5
  "bin": {
6
6
  "zero-slop": "bin/zero-slop.mjs"
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env python3
2
+ """Conservative, deterministic editing when an AI response is unavailable.
3
+
4
+ This is an availability path, not a substitute for the contextual editor. Every
5
+ rule removes a stock wrapper, contracts a phrase, or repairs paragraph staging.
6
+ Quoted text, code, Markdown links, URLs, names, figures, and claims are left alone.
7
+
8
+ python3 scripts/rescue.py draft.txt
9
+ cat draft.txt | python3 scripts/rescue.py -
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ from pathlib import Path
15
+ import re
16
+ import sys
17
+
18
+
19
+ MAX_INPUT_BYTES = 4 * 1024 * 1024
20
+ PROTECTED = re.compile(
21
+ r"```[\s\S]*?```|`[^`\n]+`|\[[^\]\n]+\]\([^)]+\)|https?://[^\s<]+|"
22
+ r"“[^”\n]*”|‘[^’\n]*’|\"[^\"\n]*\""
23
+ )
24
+ TOKEN = re.compile(r"\ue000(\d+)\ue001")
25
+
26
+
27
+ def _sentence_pair(match: re.Match[str], *, past: bool) -> str:
28
+ first, second = match.group(1), match.group(2)
29
+ lead = first[:1].upper() + first[1:]
30
+ return f"{lead} {'mattered' if past else 'matters'}. " + (
31
+ f"More important was {second}." if past else f"The larger gain is {second}."
32
+ )
33
+
34
+
35
+ def rescue_text(text: str) -> str:
36
+ """Return a bounded, source-preserving edit; return clean text unchanged."""
37
+ original = str(text or "").strip()
38
+ protected: list[str] = []
39
+
40
+ def mask(match: re.Match[str]) -> str:
41
+ protected.append(match.group(0))
42
+ return f"\ue000{len(protected) - 1}\ue001"
43
+
44
+ masked = PROTECTED.sub(mask, original)
45
+ out = re.sub(r"[\u00a0\u202f]", " ", masked)
46
+ rules: list[tuple[str, str | object]] = [
47
+ (
48
+ r"\bwe are thrilled to unveil ([^,\n]+),\s+a transformative release that "
49
+ r"redefines what is possible in ([^.]+)\.",
50
+ lambda m: f"{m.group(1)} updates {m.group(2)}.",
51
+ ),
52
+ (r"\bthis release represents a significant milestone in our journey to empower "
53
+ r"teams everywhere\.\s*", ""),
54
+ (r"\bwe have listened carefully to your feedback and are excited to deliver a "
55
+ r"suite of powerful new capabilities\.",
56
+ "We listened to your feedback and added new capabilities."),
57
+ (r"\bour cutting[-\u2010\u2011 ]edge\b", "Our"),
58
+ (r"\bhours of tedious manual configuration\b", "hours of manual configuration"),
59
+ (r"\bwe have completely reimagined\b", "We rebuilt"),
60
+ (r"\bwith robust error handling built in from the ground up\b",
61
+ "with built-in error handling"),
62
+ (r"\bwe believe these improvements will fundamentally transform how your team "
63
+ r"works,\s+and the release is available today\.", "The release is available today."),
64
+ (r"\bwe are incredibly excited to share(?: some news)? about\b", "We're excited about"),
65
+ (r"\bwe(?:['’]re| are) excited to share(?: some news)? about\b", "We're excited about"),
66
+ (r"\bwe are incredibly excited to share\b", "We're sharing"),
67
+ (r"\bwe(?:['’]re| are) excited to share\b", "We're excited about"),
68
+ (r"\bi(?:['’]m| am) incredibly excited to (?:share|announce)\b", "I'm sharing"),
69
+ (r"\bour journey\b", "our work"),
70
+ (r"\bour transformative journey\b", "our work"),
71
+ (r"\bin today'?s rapidly evolving (?:landscape|world)\b", "Today"),
72
+ (r"\bit is important to note that\b", ""),
73
+ (r"\bit is worth noting that\b", ""),
74
+ (r"\bwhat we did not realize was just how deeply it impacted everything downstream\.",
75
+ "We underestimated its effect on the work that followed."),
76
+ (r"\bonboarding is not a checklist\.\s*it is a promise\.",
77
+ "We see onboarding as a promise."),
78
+ (r"\bonboarding isn['’]t a checklist\s*[-—]\s*it['’]s a promise\.",
79
+ "We see onboarding as a promise."),
80
+ (r"\bthe insights were game[-\u2011]changing\.",
81
+ "Those conversations changed our approach."),
82
+ (r"\bthe insights were (?:transformative|clear|significant):\s*",
83
+ "Those conversations showed that "),
84
+ (r"\ba platform that leverages intelligent automation to streamline the entire "
85
+ r"process end to end\b", "a platform that automates onboarding from start to finish"),
86
+ (r"\bthe results speak for themselves:\s*([a-z])",
87
+ lambda m: m.group(1).upper()),
88
+ (r"\bthe results speak for themselves\.\s*", ""),
89
+ (r"\bbut here is the thing nobody talks about\.\s*", ""),
90
+ (r"\bthe real win was not ([^.]+)\.\s*it was ([^.]+)\.",
91
+ lambda m: _sentence_pair(m, past=True)),
92
+ (r"\bthe real win isn['’]t just ([^.]+)\.\s*it['’]s ([^.]+)\.",
93
+ lambda m: _sentence_pair(m, past=False)),
94
+ (r"\bthat is the kind of impact that keeps us going\b", "That result keeps us going"),
95
+ (r"\bunlock(?:ing)? the full potential of\b", "use"),
96
+ (r"\bseamlessly integrates?\b", "integrates"),
97
+ (r"\bjust how deeply\b", "how much"),
98
+ (r"\bgame[-\u2011]changing\b", "useful"),
99
+ (r"\bcutting[-\u2010\u2011 ]edge\b", "current"),
100
+ (r"\bredefines what(?:['’]s| is) possible in\b", "updates"),
101
+ (r"\bin order to\b", "to"),
102
+ (r"\bat the end of the day\b", "ultimately"),
103
+ (r"\bwe are\b", "we're"),
104
+ (r"\bwe did not\b", "we didn't"),
105
+ (r"\bwe do not\b", "we don't"),
106
+ (r"\bi am\b", "I'm"),
107
+ (r"\bit is\b", "it's"),
108
+ (r"\bthey are\b", "they're"),
109
+ (r"\byou are\b", "you're"),
110
+ (r"\bthere is\b", "there's"),
111
+ (r"\bdoes not\b", "doesn't"),
112
+ (r"\bis not\b", "isn't"),
113
+ (r"\bare not\b", "aren't"),
114
+ (r"\bcannot\b", "can't"),
115
+ ]
116
+ for pattern, replacement in rules:
117
+ out = re.sub(pattern, replacement, out, flags=re.IGNORECASE)
118
+ out = re.sub(r"[ \t]+\n", "\n", out)
119
+ out = re.sub(r" {2,}", " ", out).strip()
120
+
121
+ if out == masked:
122
+ paragraphs = [part.strip() for part in re.split(r"\n{2,}", out) if part.strip()]
123
+ if len(paragraphs) >= 4 and all(len(part.split()) < 24 for part in paragraphs):
124
+ out = " ".join(paragraphs)
125
+
126
+ def restore(match: re.Match[str]) -> str:
127
+ index = int(match.group(1))
128
+ return protected[index] if index < len(protected) else match.group(0)
129
+
130
+ return TOKEN.sub(restore, out).strip()
131
+
132
+
133
+ def _read(path: str) -> str:
134
+ if path == "-":
135
+ raw = sys.stdin.buffer.read(MAX_INPUT_BYTES + 1)
136
+ else:
137
+ raw = Path(path).read_bytes()
138
+ if len(raw) > MAX_INPUT_BYTES:
139
+ raise SystemExit(f"input exceeds {MAX_INPUT_BYTES} bytes")
140
+ try:
141
+ return raw.decode("utf-8")
142
+ except UnicodeDecodeError as exc:
143
+ raise SystemExit(f"input is not valid UTF-8: {exc}") from exc
144
+
145
+
146
+ def main(argv: list[str] | None = None) -> int:
147
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
148
+ parser.add_argument("input", nargs="?", default="-", help="UTF-8 file, or - for stdin")
149
+ args = parser.parse_args(argv)
150
+ sys.stdout.write(rescue_text(_read(args.input)) + "\n")
151
+ return 0
152
+
153
+
154
+ if __name__ == "__main__":
155
+ raise SystemExit(main())