zero-slop 2.7.0 → 2.7.1
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 +1 -1
- package/SKILL.md +10 -1
- package/package.json +1 -1
- package/references/eval.md +34 -17
- package/scripts/register.py +69 -0
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.7.
|
|
8
|
+
<img alt="version" src="https://img.shields.io/badge/version-2.7.1-72528F">
|
|
9
9
|
</p>
|
|
10
10
|
|
|
11
11
|
Score your writing 0 to 100 for AI slop, then edit it out without changing a single fact.
|
package/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: zero-slop
|
|
3
3
|
license: MIT
|
|
4
4
|
metadata:
|
|
5
|
-
version: "2.7.
|
|
5
|
+
version: "2.7.1"
|
|
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
|
---
|
|
@@ -449,6 +449,8 @@ the most reader value. `references/rewrite-moves.md` expands each rung.
|
|
|
449
449
|
"decided". Kill participial openers ("Leveraging X, …"). Translate internal
|
|
450
450
|
workflow labels into plain language; never let evaluator or harness language
|
|
451
451
|
leak into reader-facing prose.
|
|
452
|
+
Strong claims the author owns are content, not register: cut an intensifier
|
|
453
|
+
only for a defect you can name in context, never for strength alone.
|
|
452
454
|
Prefer an explicit actor and an active verb when responsibility matters. Keep
|
|
453
455
|
passive voice when the actor is unknown, irrelevant, deliberately withheld, or
|
|
454
456
|
native to the genre; passive voice alone is not evidence of AI writing.
|
|
@@ -595,6 +597,13 @@ format, and non-prose structure. Apply these contextual checks too:
|
|
|
595
597
|
`_coverage` map by dispositioning every paragraph; the verdict fails on any
|
|
596
598
|
paragraph nobody dispositioned, exactly as it fails on an unanswered check.
|
|
597
599
|
A non-zero exit is a failed check.
|
|
600
|
+
- **The delta.** Run
|
|
601
|
+
`python3 <skill-root>/scripts/register.py --delta <original> <final>` and
|
|
602
|
+
answer for what it prints: every inserted run must restate source meaning,
|
|
603
|
+
every cut emphasis word needs a named defect, and every rewritten span passes
|
|
604
|
+
the three direction tests — purpose has not become outcome, agency has not
|
|
605
|
+
moved, a warned future has not become an asserted present. The fact gate
|
|
606
|
+
cannot see any of these; this is where a reframed claim gets caught.
|
|
598
607
|
- **Performed register.** Re-run the step 2 performed-register pass on the exact
|
|
599
608
|
final text and state the counts. An exceeded antithesis budget, or a surviving
|
|
600
609
|
significance-scaffolding sentence, is a failed check: the text returns through
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zero-slop",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.1",
|
|
4
4
|
"description": "An Agent Skill that scores AI-sounding prose 0-100 locally and rewrites it without losing a fact. Runs inside Claude Code, Codex, Cursor, Warp, Zed and other SKILL.md agents. Offline, zero dependencies, MIT.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skill",
|
package/references/eval.md
CHANGED
|
@@ -187,7 +187,13 @@ the report even when they are zero.
|
|
|
187
187
|
51. **Qualifiers survive.** Hedges, scope limits, and caveats carry the same strength
|
|
188
188
|
as the source.
|
|
189
189
|
52. **Claims not reframed.** Same names and numbers can still carry a changed
|
|
190
|
-
emphasis or implication. Compare meaning, not tokens
|
|
190
|
+
emphasis or implication. Compare meaning, not tokens, and run three direction
|
|
191
|
+
tests on every span `register.py --delta` reports as inserted or rewritten:
|
|
192
|
+
purpose has not become outcome ("changed tactics to achieve its objective" is
|
|
193
|
+
not "changed tactics until it reached its objective"); agency has not moved
|
|
194
|
+
("helps teams generate fixes" is not "generates fixes"); a warned future has
|
|
195
|
+
not become an asserted present. These are contextual comparisons against this
|
|
196
|
+
source, never a word list.
|
|
191
197
|
53. **Non-prose untouched.** Code, front matter, tables, blockquotes, identifiers,
|
|
192
198
|
paths, and heading hierarchy intact.
|
|
193
199
|
|
|
@@ -207,44 +213,55 @@ the report even when they are zero.
|
|
|
207
213
|
59. **Proportional cutting.** No compression that stripped character. Density is
|
|
208
214
|
information per word, not fewer words.
|
|
209
215
|
60. **Read aloud.** Would this sound natural read to a sharp colleague?
|
|
210
|
-
61. **
|
|
216
|
+
61. **Emphasis survives.** `register.py --delta` lists every intensifier or
|
|
217
|
+
absolute the rewrite cut from this source. Each cut needs a defect named from
|
|
218
|
+
this file, judged in its own context: puffery, hyperbole universal, filler.
|
|
219
|
+
"Too strong" is not a defect, and a falsifiable claim the author owns keeps
|
|
220
|
+
its full strength whatever word carries it. Cutting "changed overnight" from
|
|
221
|
+
a claim the author would defend cost a blind head-to-head once.
|
|
222
|
+
62. **Earned length.** `--delta` lists every run of three or more words the rewrite
|
|
223
|
+
added. Each must restate meaning already in this source for this audience; a
|
|
224
|
+
bridge or an unpacked definition can qualify, scaffolding the author never
|
|
225
|
+
wrote cannot. A rewrite longer than its original defends every insertion or
|
|
226
|
+
loses them.
|
|
227
|
+
63. **No over-correction.** AI slop was not traded for edgy slop: forced hot takes,
|
|
211
228
|
fake first person, performed candor, staccato drama.
|
|
212
229
|
|
|
213
230
|
## E. Form
|
|
214
231
|
|
|
215
|
-
|
|
216
|
-
|
|
232
|
+
64. **Returned in the format it arrived in.** A .docx comes back a .docx.
|
|
233
|
+
65. **Reader language.** No evaluator or harness vocabulary leaked into the copy:
|
|
217
234
|
candidate, artifact, overlay, gate, scorecard, burstiness.
|
|
218
|
-
|
|
235
|
+
66. **Formatting slop.** No emoji headings, decorative bold, or bullets that should be
|
|
219
236
|
prose. Count the headings over one or two sentences: four consecutive H3s each
|
|
220
237
|
wrapping a code block and one line is a section that wants to be one section.
|
|
221
|
-
|
|
238
|
+
67. **Em dashes.** None in short copy. One or two in a long draft only where they
|
|
222
239
|
clearly beat a comma, period, or parenthesis.
|
|
223
|
-
|
|
240
|
+
68. **Hyphenated modifier stacking.** "AI-powered cloud-native data-driven platform".
|
|
224
241
|
Two stacked compound modifiers in one noun phrase is a tell; keep the hyphens
|
|
225
242
|
grammar requires and cut the rest.
|
|
226
|
-
|
|
243
|
+
69. **List-label periods and bare-noun bullets.** Bullets that are fragments ending in
|
|
227
244
|
full stops, or a list of bare noun phrases with no predicate. Either make them
|
|
228
245
|
sentences or make them a real list.
|
|
229
|
-
|
|
246
|
+
70. **Numbered-list inflation.** A numbered list where order carries no meaning, or a
|
|
230
247
|
list padded to a round number. Use a bulleted list, or prose.
|
|
231
|
-
|
|
248
|
+
71. **Chatbot artifacts.** Citation markup leaks, "Regenerate response", tool URL
|
|
232
249
|
parameters such as utm_source, and any assistant scaffolding left in the copy.
|
|
233
|
-
|
|
250
|
+
72. **Attribution accurate.** The report names who did what without guessing which
|
|
234
251
|
model is running, and never implies a Zero Slop service read the draft.
|
|
235
252
|
|
|
236
253
|
## F. Process integrity
|
|
237
254
|
|
|
238
255
|
Role 8 answers this section. It is the check no single-agent eval can make.
|
|
239
256
|
|
|
240
|
-
|
|
257
|
+
73. **Roles stayed separate.** The copy desk, read-aloud pass, verification, and
|
|
241
258
|
fresh-eyes review each ran as a distinct pass.
|
|
242
|
-
|
|
243
|
-
|
|
259
|
+
74. **No self-certification.** No role graded text it generated.
|
|
260
|
+
75. **Counts reported.** Every count in section A appears in the summary, including
|
|
244
261
|
the zeros.
|
|
245
|
-
|
|
262
|
+
76. **The exact final text cleared every check.** Not an earlier draft, not a version
|
|
246
263
|
that was repaired afterward.
|
|
247
|
-
|
|
264
|
+
77. **Role 8 approved without changes.** If it changed anything, roles 5 through 8
|
|
248
265
|
ran again on the revision.
|
|
249
|
-
|
|
266
|
+
78. **Fallbacks named honestly.** If the three-round limit was reached, the report
|
|
250
267
|
says which check failed and does not describe the result as fully verified.
|
package/scripts/register.py
CHANGED
|
@@ -611,6 +611,68 @@ def verdict(text: str, answers: dict) -> tuple[int, str]:
|
|
|
611
611
|
return 0, "\n".join(out)
|
|
612
612
|
|
|
613
613
|
|
|
614
|
+
# Emphasis words: cutting one is sometimes right (puffery) and sometimes voice
|
|
615
|
+
# flattening ("changed overnight" is a falsifiable claim the author owns). The
|
|
616
|
+
# tool cannot tell which, so it reports every cut and the eval demands a defect
|
|
617
|
+
# name for each. "Too strong" is not a defect.
|
|
618
|
+
EMPHASIS = {
|
|
619
|
+
"overnight", "never", "always", "every", "entire", "all", "nothing",
|
|
620
|
+
"worst", "best", "first", "only", "immediately", "instantly", "forever",
|
|
621
|
+
"massive", "enormous", "catastrophically", "obsessively", "extraordinary",
|
|
622
|
+
"unprecedented", "remarkable", "completely", "exactly",
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
def delta(original: str, rewrite: str) -> dict:
|
|
627
|
+
"""Word-level diff: what the rewrite added that the author never wrote, and
|
|
628
|
+
what emphasis it took away. Insertions are never free; each run must carry
|
|
629
|
+
meaning already in the source. A rewrite 27 words longer than the original
|
|
630
|
+
once lost a blind head-to-head on exactly this."""
|
|
631
|
+
import difflib
|
|
632
|
+
a = original.split()
|
|
633
|
+
b = rewrite.split()
|
|
634
|
+
sm = difflib.SequenceMatcher(a=[w.lower().strip(".,;:!?\"'()") for w in a],
|
|
635
|
+
b=[w.lower().strip(".,;:!?\"'()") for w in b])
|
|
636
|
+
inserted, deleted, cut_emphasis = [], [], []
|
|
637
|
+
for op, i1, i2, j1, j2 in sm.get_opcodes():
|
|
638
|
+
if op in ("insert", "replace") and j2 - j1 >= 3:
|
|
639
|
+
inserted.append(" ".join(b[j1:j2])[:90])
|
|
640
|
+
if op in ("delete", "replace"):
|
|
641
|
+
for w in a[i1:i2]:
|
|
642
|
+
if w.lower().strip(".,;:!?\"'()") in EMPHASIS:
|
|
643
|
+
ctx = " ".join(a[max(0, i1 - 4):min(len(a), i2 + 4)])
|
|
644
|
+
cut_emphasis.append(f"{w} ({ctx[:70]})")
|
|
645
|
+
if op == "delete" and i2 - i1 >= 3:
|
|
646
|
+
deleted.append(" ".join(a[i1:i2])[:90])
|
|
647
|
+
return {
|
|
648
|
+
"original_words": len(a), "rewrite_words": len(b),
|
|
649
|
+
"net": len(b) - len(a),
|
|
650
|
+
"inserted_runs": inserted[:12], "deleted_runs": deleted[:12],
|
|
651
|
+
"cut_emphasis": cut_emphasis[:12],
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def render_delta(d: dict) -> str:
|
|
656
|
+
out = [f"Length: {d['original_words']} -> {d['rewrite_words']} words "
|
|
657
|
+
f"({'+' if d['net'] >= 0 else ''}{d['net']})"]
|
|
658
|
+
if d["net"] > 0:
|
|
659
|
+
out.append(" The rewrite is LONGER than the original. Every inserted run below")
|
|
660
|
+
out.append(" must carry meaning already in the source, or it goes.")
|
|
661
|
+
out.append("")
|
|
662
|
+
out.append(f" Inserted runs the author never wrote ({len(d['inserted_runs'])}):")
|
|
663
|
+
for r in d["inserted_runs"] or ["(none)"]:
|
|
664
|
+
out.append(f" + {r}")
|
|
665
|
+
out.append(f" Cut emphasis, each needs a defect name, not 'too strong' ({len(d['cut_emphasis'])}):")
|
|
666
|
+
for r in d["cut_emphasis"] or ["(none)"]:
|
|
667
|
+
out.append(f" - {r}")
|
|
668
|
+
if d["deleted_runs"]:
|
|
669
|
+
out.append(f" Deleted runs ({len(d['deleted_runs'])}):")
|
|
670
|
+
for r in d["deleted_runs"]:
|
|
671
|
+
out.append(f" - {r}")
|
|
672
|
+
return "\n".join(out)
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
|
|
614
676
|
MUST_FLAG = EVAL_PATH.resolve().parent.parent / "data" / "corpus" / "must-flag"
|
|
615
677
|
|
|
616
678
|
|
|
@@ -736,6 +798,8 @@ def main() -> int:
|
|
|
736
798
|
ap.add_argument("--gate", action="store_true", help="exit 1 when any rate is over budget")
|
|
737
799
|
ap.add_argument("--calibrate", metavar="DIR", help="recompute budgets from a human corpus")
|
|
738
800
|
ap.add_argument("--selftest", action="store_true", help="check the gate against the checklist")
|
|
801
|
+
ap.add_argument("--delta", nargs=2, metavar=("ORIGINAL", "REWRITE"),
|
|
802
|
+
help="what the rewrite inserted, and what emphasis it cut")
|
|
739
803
|
ap.add_argument("--recall", action="store_true", help="verify every recorded miss in data/corpus/must-flag still gets caught")
|
|
740
804
|
ap.add_argument("--read", action="store_true", help="emit the reading brief for the host model")
|
|
741
805
|
ap.add_argument("--verdict", metavar="ANSWERS_JSON", help="gate on the measured rates plus the model's answers")
|
|
@@ -745,6 +809,11 @@ def main() -> int:
|
|
|
745
809
|
return _selftest()
|
|
746
810
|
if args.recall:
|
|
747
811
|
return recall()
|
|
812
|
+
if args.delta:
|
|
813
|
+
a = pathlib.Path(args.delta[0]).read_text(encoding="utf-8", errors="ignore")
|
|
814
|
+
b = pathlib.Path(args.delta[1]).read_text(encoding="utf-8", errors="ignore")
|
|
815
|
+
print(render_delta(delta(a, b)))
|
|
816
|
+
return 0
|
|
748
817
|
if args.calibrate:
|
|
749
818
|
calibrate(args.calibrate)
|
|
750
819
|
return 0
|