Two files, one repository
MohamedAbdallah-14/unslop ships 7 formats across 7 indexed files. The question worth asking is whether the second one says anything the first does not.
CompareAGENTS.md ↔ CLAUDE.mdAGENTS.md ↔ GEMINI.mdAGENTS.md ↔ Cline rulesAGENTS.md ↔ Copilot instructionsAGENTS.md ↔ Cursor rulesAGENTS.md ↔ Windsurf rulesCLAUDE.md ↔ GEMINI.mdCLAUDE.md ↔ Cline rulesCLAUDE.md ↔ Copilot instructionsCLAUDE.md ↔ Cursor rulesCLAUDE.md ↔ Windsurf rulesGEMINI.md ↔ Cline rulesGEMINI.md ↔ Copilot instructionsGEMINI.md ↔ Cursor rulesGEMINI.md ↔ Windsurf rulesCline rules ↔ Copilot instructionsCline rules ↔ Cursor rulesCline rules ↔ Windsurf rulesCopilot instructions ↔ Cursor rulesCopilot instructions ↔ Windsurf rulesCursor rules ↔ Windsurf rules
| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 0 | 7 | 17 | 0% |
| Commands | 1 | 0 | 1 | 50% |
| Section tags | 1 | 3 | 4 | 13% |
What each file covers
Sections
0 shared · 7 only in A · 17 only in B- − Agent Instructions — unslop repo
- − Workflow
- − Tone
- − Commits
- − Reviews
- − Tests are the contract
- − Relationship to CLAUDE.md
- + Unslop — Maintainer Guide
- + README is a product artifact
- + What this repo is
- + Source of Truth
- + Mirrored locations (do NOT edit by hand)
- + Hooks
- + `hooks/unslop-config.js` — shared module
- + `hooks/unslop-activate.js` — SessionStart hook
- + `hooks/unslop-mode-tracker.js` — UserPromptSubmit hook
- + `hooks/unslop-statusline.sh` — Statusline badge
- + Hook installation
- + Skills
- + unslop Python package
- + Tests
- + Adding a sub-skill
- + Voice
- + Security
Commands
1 shared · 0 only in A · 1 only in B- + pip install torch transformers
- python3 -m pytest tests/unslop/
Section tags
1 shared · 3 only in A · 4 only in B- − git-pr
- − api
- − agent-behaviour
- + setup
- + security
- + do-not
- + docs
- test
Line diff
MohamedAbdallah-14/unslop · AGENTS.md
@@ −1 @@
1# Agent Instructions — unslop repo
2
3Drop AI-isms. Keep the technical substance exact. Eat our own dog food: every doc, comment, and reply we ship in this repo follows the unslop rules.
4
5## Workflow
6
71. Read [`CLAUDE.md`](./CLAUDE.md) for the full maintainer guide.
82. Edit only the SSOT files. Mirrors are auto-generated by CI.
93. Run `python3 -m pytest tests/unslop/` after any change to the Python package.
104. Run `bash hooks/install.sh` after editing any hook script and verify the `.unslop-active` flag updates correctly with `/unslop full` and `stop unslop`.
11
12## Tone
13
14- No "Great question!" openers. No "I'd be happy to help."
15- No "delve", "tapestry", "testament", "leverage" (as filler), "robust" (as filler), "seamless", "holistic", "comprehensive" (as filler).
16- No "It's important to note that". No "Generally speaking". No "In essence".
17- No five-paragraph essay shape in commit messages or PR descriptions.
18- Burstiness: short. Then one longer sentence that develops the point. Short again.
19- Real uncertainty is fine ("I think", "probably") when honest. Performative softening isn't.
20
21## Commits
22
23Use `unslop-commit` style (Conventional Commits, body only when "why" isn't obvious from subject). See `skills/unslop-commit/SKILL.md`.
24
25## Reviews
26
27Use `unslop-review` style (file:line, severity prefix, observation, fix). See `skills/unslop-review/SKILL.md`.
28
29## Tests are the contract
30
31The `TestPreservation` suite enforces that humanization never modifies code, URLs, or headings. Don't disable it. If a test fails, the unslop is wrong, not the test.
32
33## Relationship to CLAUDE.md
34
35This file is the short cross-IDE companion to `CLAUDE.md`. Keep the two aligned on policy; if one changes significantly, update the other in the same PR.
36
MohamedAbdallah-14/unslop · CLAUDE.md
@@ +1 @@
1# Unslop — Maintainer Guide
2
3This file is for agents and maintainers working on the unslop plugin itself. End-user docs live in [`README.md`](./README.md).
4
5## README is a product artifact
6
7The README is the product front door. Non-technical people read it to decide whether unslop is worth installing. Treat it like UI copy, not internal docs.
8
9**Rules for any README change:**
10
11- Readable by non-AI-agent users. If you write "SessionStart hook injects system context", that phrase is invisible to most readers — translate it ("loads the unslop rules every time you open Claude Code").
12- Keep the **Before / After** examples first. That is the pitch. Cutting them or burying them under prose breaks the conversion path.
13- Install table must stay accurate. One broken install command costs a real user. Re-test every command in the install table on every PR that touches install scripts, plugin manifests, or hook paths.
14- The "What unslop does" feature list must sync with the actual code. If a rule ships or gets removed in `unslop/scripts/humanize.py`, update the README in the same PR.
15- Preserve voice. The README's terse, no-AI-ism style is intentional — it is unslop eating its own dog food. If you find a stock phrase ("comprehensive solution", "leverages cutting-edge"), a sycophancy opener ("Great question!"), or a hedging stack ("It's important to note that"), fix it in the same commit.
16- Benchmark numbers come from real runs in `benchmarks/` and `evals/`. Never invent or round. Re-run the suite if you doubt a number you're about to print.
17- Adding a new IDE/agent integration to the install table: also add a per-agent detail block in the appropriate `<details>` section so the table itself stays scannable.
18- Readability check before any README commit: would a non-programmer understand the value prop and complete the install within 60 seconds? If not, simplify or move detail into `GETTING_STARTED.md`.
19
20## What this repo is
21
22A multi-platform plugin that makes assistant output sound human. Single source of truth (SSOT) files are synced to mirrored locations (Cursor, Windsurf, Codex bundle, etc.) by `scripts/sync-mirrors.sh` on push via `.github/workflows/sync.yml`.
23
24## Source of Truth
25
26Three files are authoritative. Every other location with the same name is a mirror, generated by `scripts/sync-mirrors.sh` (run by `.github/workflows/sync.yml`). **Edit the SSOT only.**
27
28| Concern | SSOT |
29|---------|------|
30| Main unslop skill | `skills/unslop/SKILL.md` |
31| Always-on activation rule | `rules/unslop-activate.md` |
32| File-rewriter skill | `unslop/SKILL.md` |
33
34Sub-skills (`unslop-commit`, `unslop-review`, `unslop-help`) are also authoritative under `skills/<name>/SKILL.md`.
35
36## Mirrored locations (do NOT edit by hand)
37
38- `unslop/SKILL.md` (top-level)
39- `plugins/unslop/skills/unslop/SKILL.md`
40- `.cursor/skills/unslop/SKILL.md`
41- `.windsurf/skills/unslop/SKILL.md`
42- `.cursor/rules/unslop.mdc`
43- `.windsurf/rules/unslop.md`
44- `.clinerules/unslop.md`
45- `.github/copilot-instructions.md`
46- `skills/unslop-file/SKILL.md`
47- `skills/unslop-file/scripts/**`
48- `plugins/unslop/skills/unslop-file/SKILL.md`
49- `plugins/unslop/skills/unslop-file/scripts/**`
50
51The sync workflow handles all of these. If you edit a mirror, the next sync silently overwrites your change.
52
53## Hooks
54
55Four hook files live in `hooks/` plus a `unslop-config.js` shared module and a `package.json` CommonJS marker. Communicate via flag file at `$CLAUDE_CONFIG_DIR/.unslop-active` (falls back to `~/.claude/.unslop-active`).
56
57```
58SessionStart hook ──writes mode──▶ $CLAUDE_CONFIG_DIR/.unslop-active ◀──writes mode── UserPromptSubmit hook
59 │
60 reads
61 ▼
62 unslop-statusline.sh
63 [unslop] / [unslop:FULL] / ...
64```
65
66`hooks/package.json` pins the directory to `{"type": "commonjs"}` so the `.js` hooks resolve as CJS even when an ancestor `package.json` (e.g. `~/.claude/package.json` from another plugin) declares `"type": "module"`. Without this, `require()` blows up with `ReferenceError: require is not defined in ES module scope`.
67
68All hooks honor `CLAUDE_CONFIG_DIR` for non-default Claude Code config locations.
69
70### `hooks/unslop-config.js` — shared module
71
72Exports:
73- `getDefaultMode()` — resolves default mode from `UNSLOP_DEFAULT_MODE` env var, then `$XDG_CONFIG_HOME/unslop/config.json` / `~/.config/unslop/config.json` / `%APPDATA%\unslop\config.json`, then `'balanced'`
74- `safeWriteFlag(flagPath, content)` — symlink-safe flag write. Refuses if flag target or its immediate parent is a symlink. Opens with `O_NOFOLLOW` where supported. Atomic temp + rename. Creates with `0600`. Protects against local attackers replacing the predictable flag path with a symlink to clobber files writable by the user. Used by both write hooks. Silent-fails on all filesystem errors.
75- `readFlag(flagPath)` — symlink-safe, size-capped (64 bytes), whitelist-validated flag read. Returns null on any anomaly. Used by mode tracker's per-turn reinforcement to avoid injecting untrusted bytes into model context.
76- `getFlagPath()` — resolves `$CLAUDE_CONFIG_DIR/.unslop-active`.
77
78### `hooks/unslop-activate.js` — SessionStart hook
79
80Runs once per Claude Code session start. Three things:
811. Writes the active mode to `$CLAUDE_CONFIG_DIR/.unslop-active` via `safeWriteFlag` (creates if missing)
822. Emits unslop ruleset as hidden stdout — reads `skills/unslop/SKILL.md` at runtime, falls back to `rules/unslop-activate.md`, then to hardcoded rules
833. Checks `settings.json` for statusline config; if missing, appends nudge to offer setup on first interaction
84
85Silent-fails on all filesystem errors — never blocks session start.
86
87### `hooks/unslop-mode-tracker.js` — UserPromptSubmit hook
88
89Reads JSON from stdin. Three responsibilities:
90
91**1. Slash-command activation.** If prompt starts with `/unslop`, writes mode to flag file via `safeWriteFlag`:
92- `/unslop` → configured default (see `unslop-config.js`, defaults to `balanced`)
93- `/unslop subtle` → `subtle`
94- `/unslop balanced` → `balanced`
95- `/unslop full` → `full`
96- `/unslop voice-match` → `voice-match`
97- `/unslop anti-detector` → `anti-detector`
98- `/unslop-commit` → `commit`
99- `/unslop-review` → `review`
100
101**2. Natural-language activation/deactivation.** Matches phrases like "activate unslop", "turn on unslop mode", "humanize this", "make this sound human" and writes the configured default mode. Matches "stop unslop", "disable unslop", "normal mode", "robotic mode" and deletes the flag file.
102
103**3. Per-turn reinforcement.** When flag is set to a non-independent mode (i.e. not `commit`/`review`), emits a small `hookSpecificOutput` JSON reminder so the model keeps unslop style after other plugins inject competing instructions mid-conversation. The full ruleset comes from SessionStart — this is an attention anchor.
104
105### `hooks/unslop-statusline.sh` — Statusline badge
106
107Reads flag file at `$CLAUDE_CONFIG_DIR/.unslop-active`. Security hardened: refuses symlinks, caps read at 64 bytes, strips non-alphanumeric characters, whitelists valid modes. Outputs colored badge string for Claude Code statusline:
108- `balanced` or empty → `[unslop]` (green)
109- Anything else → `[unslop:<MODE_UPPERCASED>]` (green)
110
111PowerShell counterpart at `hooks/unslop-statusline.ps1` for Windows.
112
113### Hook installation
114
115**Plugin install** — hooks wired automatically by plugin system.
116
117**Standalone install** — `hooks/install.sh` (macOS/Linux) or `hooks/install.ps1` (Windows) copies hook files into `$CLAUDE_CONFIG_DIR/hooks/` and patches `settings.json` to register SessionStart and UserPromptSubmit hooks plus statusline. Supports `--force` for reinstall, checks idempotency, backs up settings.json.
118
119**Uninstall** — `hooks/uninstall.sh` / `hooks/uninstall.ps1` removes hook files and patches settings.json.
120
121**Hook safety rules:**
122
123- Hooks must never break the session. Catch all errors; silent-fail on filesystem errors.
124- Hooks must be fast (<200ms). No network calls. No reading large files.
125- Hooks must work on first install with no config. Config is opt-in.
126- Any new flag file write must go through `safeWriteFlag()` in `unslop-config.js`. Direct `fs.writeFileSync` on predictable user-owned paths reopens the symlink-clobber attack surface.
127- Hooks must respect `CLAUDE_CONFIG_DIR` env var, not hardcode `~/.claude`. Same for install/uninstall/statusline scripts.
128
129## Skills
130
131Markdown files with YAML frontmatter:
132
133```yaml
134---
135name: unslop
136description: >
137 ...
138---
139```
140
141`description` is the activation hint shown to the model. Keep it ≤200 words and include trigger phrases.
142
143The body is the operational spec. Sections we use consistently:
144
145- `## Persistence` — when the skill turns on/off
146- `## Rules` — what to drop, what to keep
147- `## Intensity` — modes and their effects
148- `## Auto-Clarity` — when to suspend unslop style
149- `## Boundaries` — hard limits
150
151## unslop Python package
152
153Lives in `unslop/scripts/`. The package is split:
154
155- `cli.py` — argparse-style CLI (no external deps)
156- `detect.py` — file type and sensitivity detection
157- `humanize.py` — core logic (deterministic regex + LLM mode)
158- `validate.py` — preserve check (code/URL/heading) + AI-ism residual check
159- `benchmark.py` — quality benchmark across a directory of samples
160- `reasoning.py` — agent reasoning-trace stripper (`<thinking>`, `## Reasoning`). Opt-in via `--strip-reasoning`
161- `surprisal.py` — optional local-LM DivEye reading. Opt-in via `--surprisal-variance`; requires `pip install torch transformers`
162- `stylometry.py` — deterministic voice-match signal extraction (incl. DivEye proxies)
163- `style_memory.py` — persisted voice profile storage (security-hardened)
164- `structural.py` — Phase 1 structural pass
165- `soul.py` — Phase 5 soul / contraction pass
166- `detector.py` — optional AI-text detector feedback loop
167- `fetch_detectors.py` — one-shot HF weight fetcher for `detector.py`
168
169**Add a new AI-ism**: edit `STOCK_VOCAB`, `HEDGING_OPENERS`, `SYCOPHANCY`, `PERFORMATIVE`, or `TRANSITION_TICS` in `humanize.py` AND add a corresponding pattern to `AI_ISMS` in `validate.py` so the validator catches additions. Add a test in `tests/unslop/test_humanize.py`.
170
171**Preservation is non-negotiable.** Every regex must run inside `_protect()` placeholders or be guaranteed to match only natural-language prose. `_protect` currently covers, in order: YAML frontmatter, fenced code, indented code, tables, blockquotes, headings, markdown links, inline code, bare URLs, quoted single-word examples (`"delve"`, use/mention distinction). The `TestPreservation` suite in `test_humanize.py` is the contract; the validator in `validate.py` byte-compares each protected category between input and output and fails the run on any mutation.
172
173**Per-paragraph em-dash cap has a list carve-out.** `_cap_em_dashes_per_paragraph` splits on blank lines, then for paragraphs that are pure lists (every non-blank line starts with `-`, `*`, `+`, or `N.`) it treats each list item as its own em-dash paragraph. Without this, a 5-bullet list where each bullet used a single em-dash would silently lose the dashes in bullets 3–5.
174
175## Tests
176
177```bash
178python3 -m pytest tests/unslop/
179```
180
181The deterministic suite must pass on every change. LLM tests are opt-in via `UNSLOP_RUN_LLM_TESTS=1` (cost API credits).
182
183## Adding a sub-skill
184
1851. Create `skills/<name>/SKILL.md` with frontmatter + body.
1862. Create `commands/<name>.toml` if it gets a slash command.
1873. Add the skill to the host manifest only when that host requires explicit skill or command entries. Claude Code hooks currently discover the repo's `skills/` and `commands/` files without listing them in `.claude-plugin/plugin.json`.
1884. Add sync mappings in `scripts/sync-mirrors.sh`, then add trigger paths in `.github/workflows/sync.yml`.
1895. Document the skill in `skills/unslop-help/SKILL.md` (the reference card).
1906. Add a row to the README sub-skills table.
191
192## Voice
193
194The unslop voice we keep in our own docs: direct, specific, kind, no AI-isms. We eat our own dog food. If the README opens with "Great question!", we have failed.
195
196## Security
197
198- The Python scripts refuse sensitive paths (`.env*`, `*.pem`, `~/.ssh/`, etc.) before any read or API call.
199- LLM mode optionally subprocesses the `claude` CLI when no `ANTHROPIC_API_KEY` is set. `subprocess.run` is always called with `shell=False` and a fixed argument list.
200- File size cap: 500 KB. See `unslop/scripts/detect.py`.
201- See `unslop/SECURITY.md` for the full Snyk rationale.
202
@@ −1 +1 @@
1−# Agent Instructions — unslop repo
1+# Unslop — Maintainer Guide
22
3−Drop AI-isms. Keep the technical substance exact. Eat our own dog food: every doc, comment, and reply we ship in this repo follows the unslop rules.
3+This file is for agents and maintainers working on the unslop plugin itself. End-user docs live in [`README.md`](./README.md).
44
5−## Workflow
5+## README is a product artifact
66
7−1. Read [`CLAUDE.md`](./CLAUDE.md) for the full maintainer guide.
8−2. Edit only the SSOT files. Mirrors are auto-generated by CI.
9−3. Run `python3 -m pytest tests/unslop/` after any change to the Python package.
10−4. Run `bash hooks/install.sh` after editing any hook script and verify the `.unslop-active` flag updates correctly with `/unslop full` and `stop unslop`.
7+The README is the product front door. Non-technical people read it to decide whether unslop is worth installing. Treat it like UI copy, not internal docs.
118
12−## Tone
9+**Rules for any README change:**
1310
14−- No "Great question!" openers. No "I'd be happy to help."
15−- No "delve", "tapestry", "testament", "leverage" (as filler), "robust" (as filler), "seamless", "holistic", "comprehensive" (as filler).
16−- No "It's important to note that". No "Generally speaking". No "In essence".
17−- No five-paragraph essay shape in commit messages or PR descriptions.
18−- Burstiness: short. Then one longer sentence that develops the point. Short again.
19−- Real uncertainty is fine ("I think", "probably") when honest. Performative softening isn't.
11+- Readable by non-AI-agent users. If you write "SessionStart hook injects system context", that phrase is invisible to most readers — translate it ("loads the unslop rules every time you open Claude Code").
12+- Keep the **Before / After** examples first. That is the pitch. Cutting them or burying them under prose breaks the conversion path.
13+- Install table must stay accurate. One broken install command costs a real user. Re-test every command in the install table on every PR that touches install scripts, plugin manifests, or hook paths.
14+- The "What unslop does" feature list must sync with the actual code. If a rule ships or gets removed in `unslop/scripts/humanize.py`, update the README in the same PR.
15+- Preserve voice. The README's terse, no-AI-ism style is intentional — it is unslop eating its own dog food. If you find a stock phrase ("comprehensive solution", "leverages cutting-edge"), a sycophancy opener ("Great question!"), or a hedging stack ("It's important to note that"), fix it in the same commit.
16+- Benchmark numbers come from real runs in `benchmarks/` and `evals/`. Never invent or round. Re-run the suite if you doubt a number you're about to print.
17+- Adding a new IDE/agent integration to the install table: also add a per-agent detail block in the appropriate `<details>` section so the table itself stays scannable.
18+- Readability check before any README commit: would a non-programmer understand the value prop and complete the install within 60 seconds? If not, simplify or move detail into `GETTING_STARTED.md`.
2019
21−## Commits
20+## What this repo is
2221
23−Use `unslop-commit` style (Conventional Commits, body only when "why" isn't obvious from subject). See `skills/unslop-commit/SKILL.md`.
22+A multi-platform plugin that makes assistant output sound human. Single source of truth (SSOT) files are synced to mirrored locations (Cursor, Windsurf, Codex bundle, etc.) by `scripts/sync-mirrors.sh` on push via `.github/workflows/sync.yml`.
2423
25−## Reviews
24+## Source of Truth
2625
27−Use `unslop-review` style (file:line, severity prefix, observation, fix). See `skills/unslop-review/SKILL.md`.
26+Three files are authoritative. Every other location with the same name is a mirror, generated by `scripts/sync-mirrors.sh` (run by `.github/workflows/sync.yml`). **Edit the SSOT only.**
2827
29−## Tests are the contract
28+| Concern | SSOT |
29+|---------|------|
30+| Main unslop skill | `skills/unslop/SKILL.md` |
31+| Always-on activation rule | `rules/unslop-activate.md` |
32+| File-rewriter skill | `unslop/SKILL.md` |
3033
31−The `TestPreservation` suite enforces that humanization never modifies code, URLs, or headings. Don't disable it. If a test fails, the unslop is wrong, not the test.
34+Sub-skills (`unslop-commit`, `unslop-review`, `unslop-help`) are also authoritative under `skills/<name>/SKILL.md`.
3235
33−## Relationship to CLAUDE.md
36+## Mirrored locations (do NOT edit by hand)
3437
35−This file is the short cross-IDE companion to `CLAUDE.md`. Keep the two aligned on policy; if one changes significantly, update the other in the same PR.
38+- `unslop/SKILL.md` (top-level)
39+- `plugins/unslop/skills/unslop/SKILL.md`
40+- `.cursor/skills/unslop/SKILL.md`
41+- `.windsurf/skills/unslop/SKILL.md`
42+- `.cursor/rules/unslop.mdc`
43+- `.windsurf/rules/unslop.md`
44+- `.clinerules/unslop.md`
45+- `.github/copilot-instructions.md`
46+- `skills/unslop-file/SKILL.md`
47+- `skills/unslop-file/scripts/**`
48+- `plugins/unslop/skills/unslop-file/SKILL.md`
49+- `plugins/unslop/skills/unslop-file/scripts/**`
50+
51+The sync workflow handles all of these. If you edit a mirror, the next sync silently overwrites your change.
52+
53+## Hooks
54+
55+Four hook files live in `hooks/` plus a `unslop-config.js` shared module and a `package.json` CommonJS marker. Communicate via flag file at `$CLAUDE_CONFIG_DIR/.unslop-active` (falls back to `~/.claude/.unslop-active`).
56+
57+```
58+SessionStart hook ──writes mode──▶ $CLAUDE_CONFIG_DIR/.unslop-active ◀──writes mode── UserPromptSubmit hook
59+ │
60+ reads
61+ ▼
62+ unslop-statusline.sh
63+ [unslop] / [unslop:FULL] / ...
64+```
65+
66+`hooks/package.json` pins the directory to `{"type": "commonjs"}` so the `.js` hooks resolve as CJS even when an ancestor `package.json` (e.g. `~/.claude/package.json` from another plugin) declares `"type": "module"`. Without this, `require()` blows up with `ReferenceError: require is not defined in ES module scope`.
67+
68+All hooks honor `CLAUDE_CONFIG_DIR` for non-default Claude Code config locations.
69+
70+### `hooks/unslop-config.js` — shared module
71+
72+Exports:
73+- `getDefaultMode()` — resolves default mode from `UNSLOP_DEFAULT_MODE` env var, then `$XDG_CONFIG_HOME/unslop/config.json` / `~/.config/unslop/config.json` / `%APPDATA%\unslop\config.json`, then `'balanced'`
74+- `safeWriteFlag(flagPath, content)` — symlink-safe flag write. Refuses if flag target or its immediate parent is a symlink. Opens with `O_NOFOLLOW` where supported. Atomic temp + rename. Creates with `0600`. Protects against local attackers replacing the predictable flag path with a symlink to clobber files writable by the user. Used by both write hooks. Silent-fails on all filesystem errors.
75+- `readFlag(flagPath)` — symlink-safe, size-capped (64 bytes), whitelist-validated flag read. Returns null on any anomaly. Used by mode tracker's per-turn reinforcement to avoid injecting untrusted bytes into model context.
76+- `getFlagPath()` — resolves `$CLAUDE_CONFIG_DIR/.unslop-active`.
77+
78+### `hooks/unslop-activate.js` — SessionStart hook
79+
80+Runs once per Claude Code session start. Three things:
81+1. Writes the active mode to `$CLAUDE_CONFIG_DIR/.unslop-active` via `safeWriteFlag` (creates if missing)
82+2. Emits unslop ruleset as hidden stdout — reads `skills/unslop/SKILL.md` at runtime, falls back to `rules/unslop-activate.md`, then to hardcoded rules
83+3. Checks `settings.json` for statusline config; if missing, appends nudge to offer setup on first interaction
84+
85+Silent-fails on all filesystem errors — never blocks session start.
86+
87+### `hooks/unslop-mode-tracker.js` — UserPromptSubmit hook
88+
89+Reads JSON from stdin. Three responsibilities:
90+
91+**1. Slash-command activation.** If prompt starts with `/unslop`, writes mode to flag file via `safeWriteFlag`:
92+- `/unslop` → configured default (see `unslop-config.js`, defaults to `balanced`)
93+- `/unslop subtle` → `subtle`
94+- `/unslop balanced` → `balanced`
95+- `/unslop full` → `full`
96+- `/unslop voice-match` → `voice-match`
97+- `/unslop anti-detector` → `anti-detector`
98+- `/unslop-commit` → `commit`
99+- `/unslop-review` → `review`
100+
101+**2. Natural-language activation/deactivation.** Matches phrases like "activate unslop", "turn on unslop mode", "humanize this", "make this sound human" and writes the configured default mode. Matches "stop unslop", "disable unslop", "normal mode", "robotic mode" and deletes the flag file.
102+
103+**3. Per-turn reinforcement.** When flag is set to a non-independent mode (i.e. not `commit`/`review`), emits a small `hookSpecificOutput` JSON reminder so the model keeps unslop style after other plugins inject competing instructions mid-conversation. The full ruleset comes from SessionStart — this is an attention anchor.
104+
105+### `hooks/unslop-statusline.sh` — Statusline badge
106+
107+Reads flag file at `$CLAUDE_CONFIG_DIR/.unslop-active`. Security hardened: refuses symlinks, caps read at 64 bytes, strips non-alphanumeric characters, whitelists valid modes. Outputs colored badge string for Claude Code statusline:
108+- `balanced` or empty → `[unslop]` (green)
109+- Anything else → `[unslop:<MODE_UPPERCASED>]` (green)
110+
111+PowerShell counterpart at `hooks/unslop-statusline.ps1` for Windows.
112+
113+### Hook installation
114+
115+**Plugin install** — hooks wired automatically by plugin system.
116+
117+**Standalone install** — `hooks/install.sh` (macOS/Linux) or `hooks/install.ps1` (Windows) copies hook files into `$CLAUDE_CONFIG_DIR/hooks/` and patches `settings.json` to register SessionStart and UserPromptSubmit hooks plus statusline. Supports `--force` for reinstall, checks idempotency, backs up settings.json.
118+
119+**Uninstall** — `hooks/uninstall.sh` / `hooks/uninstall.ps1` removes hook files and patches settings.json.
120+
121+**Hook safety rules:**
122+
123+- Hooks must never break the session. Catch all errors; silent-fail on filesystem errors.
124+- Hooks must be fast (<200ms). No network calls. No reading large files.
125+- Hooks must work on first install with no config. Config is opt-in.
126+- Any new flag file write must go through `safeWriteFlag()` in `unslop-config.js`. Direct `fs.writeFileSync` on predictable user-owned paths reopens the symlink-clobber attack surface.
127+- Hooks must respect `CLAUDE_CONFIG_DIR` env var, not hardcode `~/.claude`. Same for install/uninstall/statusline scripts.
128+
129+## Skills
130+
131+Markdown files with YAML frontmatter:
132+
133+```yaml
134+---
135+name: unslop
136+description: >
137+ ...
138+---
139+```
140+
141+`description` is the activation hint shown to the model. Keep it ≤200 words and include trigger phrases.
142+
143+The body is the operational spec. Sections we use consistently:
144+
145+- `## Persistence` — when the skill turns on/off
146+- `## Rules` — what to drop, what to keep
147+- `## Intensity` — modes and their effects
148+- `## Auto-Clarity` — when to suspend unslop style
149+- `## Boundaries` — hard limits
150+
151+## unslop Python package
152+
153+Lives in `unslop/scripts/`. The package is split:
154+
155+- `cli.py` — argparse-style CLI (no external deps)
156+- `detect.py` — file type and sensitivity detection
157+- `humanize.py` — core logic (deterministic regex + LLM mode)
158+- `validate.py` — preserve check (code/URL/heading) + AI-ism residual check
159+- `benchmark.py` — quality benchmark across a directory of samples
160+- `reasoning.py` — agent reasoning-trace stripper (`<thinking>`, `## Reasoning`). Opt-in via `--strip-reasoning`
161+- `surprisal.py` — optional local-LM DivEye reading. Opt-in via `--surprisal-variance`; requires `pip install torch transformers`
162+- `stylometry.py` — deterministic voice-match signal extraction (incl. DivEye proxies)
163+- `style_memory.py` — persisted voice profile storage (security-hardened)
164+- `structural.py` — Phase 1 structural pass
165+- `soul.py` — Phase 5 soul / contraction pass
166+- `detector.py` — optional AI-text detector feedback loop
167+- `fetch_detectors.py` — one-shot HF weight fetcher for `detector.py`
168+
169+**Add a new AI-ism**: edit `STOCK_VOCAB`, `HEDGING_OPENERS`, `SYCOPHANCY`, `PERFORMATIVE`, or `TRANSITION_TICS` in `humanize.py` AND add a corresponding pattern to `AI_ISMS` in `validate.py` so the validator catches additions. Add a test in `tests/unslop/test_humanize.py`.
170+
171+**Preservation is non-negotiable.** Every regex must run inside `_protect()` placeholders or be guaranteed to match only natural-language prose. `_protect` currently covers, in order: YAML frontmatter, fenced code, indented code, tables, blockquotes, headings, markdown links, inline code, bare URLs, quoted single-word examples (`"delve"`, use/mention distinction). The `TestPreservation` suite in `test_humanize.py` is the contract; the validator in `validate.py` byte-compares each protected category between input and output and fails the run on any mutation.
172+
173+**Per-paragraph em-dash cap has a list carve-out.** `_cap_em_dashes_per_paragraph` splits on blank lines, then for paragraphs that are pure lists (every non-blank line starts with `-`, `*`, `+`, or `N.`) it treats each list item as its own em-dash paragraph. Without this, a 5-bullet list where each bullet used a single em-dash would silently lose the dashes in bullets 3–5.
174+
175+## Tests
176+
177+```bash
178+python3 -m pytest tests/unslop/
179+```
180+
181+The deterministic suite must pass on every change. LLM tests are opt-in via `UNSLOP_RUN_LLM_TESTS=1` (cost API credits).
182+
183+## Adding a sub-skill
184+
185+1. Create `skills/<name>/SKILL.md` with frontmatter + body.
186+2. Create `commands/<name>.toml` if it gets a slash command.
187+3. Add the skill to the host manifest only when that host requires explicit skill or command entries. Claude Code hooks currently discover the repo's `skills/` and `commands/` files without listing them in `.claude-plugin/plugin.json`.
188+4. Add sync mappings in `scripts/sync-mirrors.sh`, then add trigger paths in `.github/workflows/sync.yml`.
189+5. Document the skill in `skills/unslop-help/SKILL.md` (the reference card).
190+6. Add a row to the README sub-skills table.
191+
192+## Voice
193+
194+The unslop voice we keep in our own docs: direct, specific, kind, no AI-isms. We eat our own dog food. If the README opens with "Great question!", we have failed.
195+
196+## Security
197+
198+- The Python scripts refuse sensitive paths (`.env*`, `*.pem`, `~/.ssh/`, etc.) before any read or API call.
199+- LLM mode optionally subprocesses the `claude` CLI when no `ANTHROPIC_API_KEY` is set. `subprocess.run` is always called with `shell=False` and a fixed argument list.
200+- File size cap: 500 KB. See `unslop/scripts/detect.py`.
201+- See `unslop/SECURITY.md` for the full Snyk rationale.
36202
