Hooks
What mohamedabdallah-14-unslop runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.
> /plugin marketplace add MohamedAbdallah-14/unslop > /plugin install unslop@unslop-marketplace
Ships with mohamedabdallah-14-unslop. Installing the plugin gets these hooks.
Where it lives
- hooks/install.ps1GitHub
Read the script
# Install unslop hooks into Claude Code's user settings on Windows. # # Copies hook scripts into $CLAUDE_CONFIG_DIR/hooks/ (or ~/.claude/hooks/) # and registers SessionStart + UserPromptSubmit hooks in settings.json. # Also wires the statusline so the [unslop] badge shows when active. # # Supports: -Force to overwrite existing hooks. # Requires: PowerShell 5.1+, Node.js param([switch]$Force) $ErrorActionPreference = 'Stop' $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $ClaudeDir = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME '.claude' } $HooksDir = Join-Path $ClaudeDir 'hooks' $Settings = Join-Path $ClaudeDir 'settings.json' if (-not (Get-Command node -ErrorAction SilentlyContinue)) { Write-Error "Error: 'node' not found on PATH. Install Node.js from https://nodejs.org" exit 1 } $HookFiles = @('package.json', 'unslop-config.js', 'unslop-activate.js', 'unslop-mode-tracker.js', 'unslop-statusline.ps1') # Check if already installed (unless -Force) if (-not $Force) { $allPresent = $true foreach ($hook in $HookFiles) { if (-not (Test-Path (Join-Path $HooksDir $hook))) { $allPresent = $false break } } if ($allPresent -and (Test-Path $Settings)) { Write-Host "Unslop hooks already installed in $HooksDir" Write-Host " Re-run with -Force to overwrite." exit 0 } } if ($Force -and (Test-Path (Join-Path $HooksDir 'unslop-activate.js'))) { Write-Host "Reinstalling unslop hooks (-Force)..." } else { Write-Host "Installing unslop hooks..." } New-Item -ItemType Directory -Force -Path $HooksDir | Out-Null foreach ($hook in $HookFiles) { $src = Join-Path $ScriptDir $hook $dst = Join-Path $HooksDir $hook if (Test-Path $src) { Copy-Item -Force $src $dst } else { $url = "https://raw.githubusercontent.com/MohamedAbdallah-14/unslop/main/hooks/$hook" Invoke-WebRequest -Uri $url -OutFile $dst } Write-Host " Installed: $dst" } if (-not (Test-Path $Settings)) { '{}' | Out-File -Encoding utf8 $Settings } # Back up settings before modifying Copy-Item $Settings "$Settings.bak" $env:UNSLOP_SETTINGS = $Settings $env:UNSLOP_HOOKS_DIR = $HooksDir node -e @" const fs = require('fs'); const settingsPath = process.env.UNSLOP_SETTINGS; const hooksDir = process.env.UNSLOP_HOOKS_DIR; const managedStatusLinePath = hooksDir + '/unslop-statusline.ps1'; const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); if (!settings.hooks) settings.hooks = {}; if (!settings.hooks.SessionStart) settings.hooks.SessionStart = []; const hasStart = settings.hooks.SessionStart.some(e => e.hooks && e.hooks.some(h => h.command && h.command.includes('unslop')) ); if (!hasStart) { settings.hooks.SessionStart.push({ hooks: [{ type: 'command', command: 'node \"' + hooksDir + '/unslop-activate.js\"', timeout: 5, statusMessage: 'Loading unslop mode...' }] }); } if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = []; const hasPrompt = settings.hooks.UserPromptSubmit.some(e => e.hooks && e.hooks.some(h => h.command && h.command.includes('unslop')) ); if (!hasPrompt) { settings.hooks.UserPromptSubmit.push({ hooks: [{ type: 'command', command: 'node \"' + hooksDir + '/unslop-mode-tracker.js\"', timeout: 5, statusMessage: 'Tracking unslop mode...' }] }); } if (!settings.statusLine) { settings.statusLine = { type: 'command', command: 'powershell -ExecutionPolicy Bypass -File \"' + managedStatusLinePath + '\"' }; console.log(' Statusline badge configured.'); } else { const cmd = typeof settings.statusLine === 'string' ? settings.statusLine : (settings.statusLine.command || ''); if (cmd.includes(managedStatusLinePath)) { console.log(' Statusline badge already configured.'); } else { console.log(' NOTE: Existing statusline detected - unslop badge NOT added.'); } } fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n'); console.log(' Hooks wired in settings.json'); "@ Write-Host "" Write-Host "Done! Restart Claude Code to activate." Write-Host "" Write-Host "What's installed:" Write-Host " - SessionStart hook: auto-loads unslop rules every session" Write-Host " - Mode tracker hook: updates statusline badge when you switch modes" Write-Host " - Statusline badge: shows [unslop] or [unslop:FULL] etc." - hooks/install.shGitHub
Read the script
#!/bin/bash # unslop — one-command hook installer for Claude Code # Installs: SessionStart hook (auto-load rules) + UserPromptSubmit hook (mode tracking) # Usage: bash hooks/install.sh # or: bash hooks/install.sh --force (re-install over existing hooks) set -e FORCE=0 for arg in "$@"; do case "$arg" in --force|-f) FORCE=1 ;; esac done case "$OSTYPE" in msys*|cygwin*|mingw*) echo "WARNING: Running on Windows ($OSTYPE)." echo " This script works in Git Bash/MSYS but symlinks may require" echo " Developer Mode or admin privileges." echo " If you installed via 'claude plugin install', you don't need this script." echo "" ;; esac if ! command -v node >/dev/null 2>&1; then echo "ERROR: 'node' is required to install the unslop hooks (used to merge" echo " the hook config into ~/.claude/settings.json safely)." echo " Install Node.js from https://nodejs.org and re-run this script." exit 1 fi CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}" HOOKS_DIR="$CLAUDE_DIR/hooks" SETTINGS="$CLAUDE_DIR/settings.json" REPO_URL="https://raw.githubusercontent.com/MohamedAbdallah-14/unslop/main/hooks" HOOK_FILES=("package.json" "unslop-config.js" "unslop-activate.js" "unslop-mode-tracker.js" "unslop-statusline.sh") SCRIPT_DIR="" if [ -n "${BASH_SOURCE[0]:-}" ] && [ -f "${BASH_SOURCE[0]}" ]; then SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd)" fi # Check if already installed (unless --force) ALREADY_INSTALLED=0 if [ "$FORCE" -eq 0 ]; then ALL_FILES_PRESENT=1 for hook in "${HOOK_FILES[@]}"; do if [ ! -f "$HOOKS_DIR/$hook" ]; then ALL_FILES_PRESENT=0 break fi done HOOKS_WIRED=0 HAS_STATUSLINE=0 if [ "$ALL_FILES_PRESENT" -eq 1 ] && [ -f "$SETTINGS" ]; then if UNSLOP_SETTINGS="$SETTINGS" node -e " const fs = require('fs'); const settings = JSON.parse(fs.readFileSync(process.env.UNSLOP_SETTINGS, 'utf8')); const hasUnslopHook = (event) => Array.isArray(settings.hooks?.[event]) && settings.hooks[event].some(e => e.hooks && e.hooks.some(h => h.command && h.command.includes('unslop')) ); process.exit( hasUnslopHook('SessionStart') && hasUnslopHook('UserPromptSubmit') && !!settings.statusLine ? 0 : 1 ); " >/dev/null 2>&1; then HOOKS_WIRED=1 HAS_STATUSLINE=1 fi fi if [ "$ALL_FILES_PRESENT" -eq 1 ] && [ "$HOOKS_WIRED" -eq 1 ] && [ "$HAS_STATUSLINE" -eq 1 ]; then ALREADY_INSTALLED=1 echo "Unslop hooks already installed in $HOOKS_DIR" echo " Re-run with --force to overwrite: bash hooks/install.sh --force" echo "" fi fi if [ "$ALREADY_INSTALLED" -eq 1 ] && [ "$FORCE" -eq 0 ]; then echo "Nothing to do. Hooks are already in place." exit 0 fi if [ "$FORCE" -eq 1 ] && [ -f "$HOOKS_DIR/unslop-activate.js" ]; then echo "Reinstalling unslop hooks (--force)..." else echo "Installing unslop hooks..." fi mkdir -p "$HOOKS_DIR" for hook in "${HOOK_FILES[@]}"; do if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/$hook" ]; then cp "$SCRIPT_DIR/$hook" "$HOOKS_DIR/$hook" else curl -fsSL "$REPO_URL/$hook" -o "$HOOKS_DIR/$hook" fi echo " Installed: $HOOKS_DIR/$hook" done chmod +x "$HOOKS_DIR/unslop-statusline.sh" if [ ! -f "$SETTINGS" ]; then echo '{}' > "$SETTINGS" fi # Back up existing settings.json before touching it cp "$SETTINGS" "$SETTINGS.bak" UNSLOP_SETTINGS="$SETTINGS" UNSLOP_HOOKS_DIR="$HOOKS_DIR" node -e " const fs = require('fs'); const settingsPath = process.env.UNSLOP_SETTINGS; const hooksDir = process.env.UNSLOP_HOOKS_DIR; const managedStatusLinePath = hooksDir + '/unslop-statusline.sh'; const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); if (!settings.hooks) settings.hooks = {}; if (!settings.hooks.SessionStart) settings.hooks.SessionStart = []; const hasStart = settings.hooks.SessionStart.some(e => e.hooks && e.hooks.some(h => h.command && h.command.includes('unslop')) ); if (!hasStart) { settings.hooks.SessionStart.push({ hooks: [{ type: 'command', command: 'node \"' + hooksDir + '/unslop-activate.js\"', timeout: 5, statusMessage: 'Loading unslop mode...' }] }); } if (!settings.hooks.UserPromptSubmit) settings.hooks.UserPromptSubmit = []; const hasPrompt = settings.hooks.UserPromptSubmit.some(e => e.hooks && e.hooks.some(h => h.command && h.command.includes('unslop')) ); if (!hasPrompt) { settings.hooks.UserPromptSubmit.push({ hooks: [{ type: 'command', command: 'node \"' + hooksDir + '/unslop-mode-tracker.js\"', timeout: 5, statusMessage: 'Tracking unslop mode...' }] }); } if (!settings.statusLine) { settings.statusLine = { type: 'command', command: 'bash \"' + managedStatusLinePath + '\"' }; console.log(' Statusline badge configured.'); } else { const cmd = typeof settings.statusLine === 'string' ? settings.statusLine : (settings.statusLine.command || ''); if (cmd.includes(managedStatusLinePath)) { console.log(' Statusline badge already configured.'); } else { console.log(' NOTE: Existing statusline detected — unslop badge NOT added.'); console.log(' See hooks/README.md to add the badge to your existing statusline.'); } } fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n'); console.log(' Hooks wired in settings.json'); " echo "" echo "Done! Restart Claude Code to activate." echo "" echo "What's installed:" echo " - SessionStart hook: auto-loads unslop rules every session" echo " - Mode tracker hook: updates statusline badge when you switch modes" echo " (/unslop subtle, /unslop full, /unslop-commit, etc.)" echo " - Statusline badge: shows [unslop] or [unslop:FULL] etc." - hooks/uninstall.ps1GitHub
Read the script
# unslop — uninstall hook files and remove settings.json entries # Usage: pwsh hooks/uninstall.ps1 $ErrorActionPreference = 'Stop' $ClaudeDir = if ($env:CLAUDE_CONFIG_DIR) { $env:CLAUDE_CONFIG_DIR } else { Join-Path $HOME '.claude' } $HooksDir = Join-Path $ClaudeDir 'hooks' $Settings = Join-Path $ClaudeDir 'settings.json' $Flag = Join-Path $ClaudeDir '.unslop-active' $HookFiles = @('package.json', 'unslop-config.js', 'unslop-activate.js', 'unslop-mode-tracker.js', 'unslop-statusline.ps1') Write-Host "Uninstalling unslop hooks..." foreach ($hook in $HookFiles) { $target = Join-Path $HooksDir $hook if (Test-Path $target) { Remove-Item $target -Force Write-Host " Removed: $target" } } if (Test-Path $Flag) { Remove-Item $Flag -Force Write-Host " Removed flag file: $Flag" } if ((Test-Path $Settings) -and (Get-Command node -ErrorAction SilentlyContinue)) { Copy-Item $Settings "$Settings.bak" $env:UNSLOP_SETTINGS = $Settings node -e @" const fs = require('fs'); const settingsPath = process.env.UNSLOP_SETTINGS; const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); if (settings.hooks) { for (const event of ['SessionStart', 'UserPromptSubmit']) { if (Array.isArray(settings.hooks[event])) { settings.hooks[event] = settings.hooks[event].filter(e => !(e.hooks && e.hooks.some(h => h.command && h.command.includes('unslop'))) ); if (settings.hooks[event].length === 0) delete settings.hooks[event]; } } if (Object.keys(settings.hooks).length === 0) delete settings.hooks; } if (settings.statusLine) { const cmd = typeof settings.statusLine === 'string' ? settings.statusLine : (settings.statusLine.command || ''); if (cmd.includes('unslop-statusline')) { delete settings.statusLine; console.log(' Removed statusline config.'); } } fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n'); console.log(' Cleaned settings.json'); "@ } else { Write-Host " Skipped settings.json cleanup (node not found or settings.json missing)." } Write-Host "" Write-Host "Done. Restart Claude Code to complete removal." - hooks/uninstall.shGitHub
Read the script
#!/bin/bash # unslop — uninstall hook files and remove settings.json entries # Usage: bash hooks/uninstall.sh set -e CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}" HOOKS_DIR="$CLAUDE_DIR/hooks" SETTINGS="$CLAUDE_DIR/settings.json" FLAG="$CLAUDE_DIR/.unslop-active" HOOK_FILES=("package.json" "unslop-config.js" "unslop-activate.js" "unslop-mode-tracker.js" "unslop-statusline.sh") echo "Uninstalling unslop hooks..." for hook in "${HOOK_FILES[@]}"; do target="$HOOKS_DIR/$hook" if [ -f "$target" ]; then rm "$target" echo " Removed: $target" fi done if [ -f "$FLAG" ]; then rm "$FLAG" echo " Removed flag file: $FLAG" fi if [ -f "$SETTINGS" ] && command -v node >/dev/null 2>&1; then cp "$SETTINGS" "$SETTINGS.bak" UNSLOP_SETTINGS="$SETTINGS" node -e " const fs = require('fs'); const settingsPath = process.env.UNSLOP_SETTINGS; const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); if (settings.hooks) { for (const event of ['SessionStart', 'UserPromptSubmit']) { if (Array.isArray(settings.hooks[event])) { settings.hooks[event] = settings.hooks[event].filter(e => !(e.hooks && e.hooks.some(h => h.command && h.command.includes('unslop'))) ); if (settings.hooks[event].length === 0) delete settings.hooks[event]; } } if (Object.keys(settings.hooks).length === 0) delete settings.hooks; } if (settings.statusLine) { const cmd = typeof settings.statusLine === 'string' ? settings.statusLine : (settings.statusLine.command || ''); if (cmd.includes('unslop-statusline')) { delete settings.statusLine; console.log(' Removed statusline config.'); } } fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n'); console.log(' Cleaned settings.json'); " else echo " Skipped settings.json cleanup (node not found or settings.json missing)." fi echo "" echo "Done. Restart Claude Code to complete removal." - hooks/unslop-activate.jsGitHub
Read the script
#!/usr/bin/env node // unslop — Claude Code SessionStart activation hook // // Runs on every session start: // 1. Writes flag file at $CLAUDE_CONFIG_DIR/.unslop-active (statusline reads this) // 2. Emits unslop ruleset as hidden SessionStart context // 3. Detects missing statusline config and emits setup nudge const fs = require('fs'); const path = require('path'); const os = require('os'); const { getDefaultMode, safeWriteFlag, getFlagPath, getTurnCounterPath, resetTurnCount, } = require('./unslop-config'); const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'); const flagPath = getFlagPath(); const counterPath = getTurnCounterPath(); const settingsPath = path.join(claudeDir, 'settings.json'); const mode = getDefaultMode(); // Persona-drift reset: a new session always starts at turn 0. RMTBench / // HorizonBench report that long contexts accumulate drift; the counter is // only meaningful within a single session, so we zero it here. resetTurnCount(counterPath); if (mode === 'off') { try { fs.unlinkSync(flagPath); } catch (e) {} process.stdout.write('OK'); process.exit(0); } safeWriteFlag(flagPath, mode); // Independent modes have their own skill files — don't emit the full ruleset. const INDEPENDENT_MODES = new Set(['commit', 'review']); if (INDEPENDENT_MODES.has(mode)) { process.stdout.write('UNSLOP MODE ACTIVE — level: ' + mode + '. Behavior defined by /unslop-' + mode + ' skill.'); process.exit(0); } // Read SKILL.md — the single source of truth for unslop behavior. // Plugin installs: __dirname = <plugin_root>/hooks/, SKILL.md at <plugin_root>/skills/unslop/SKILL.md // Standalone installs: __dirname = $CLAUDE_CONFIG_DIR/hooks/, SKILL.md won't exist — falls back to activation rule then hardcoded rules. let skillContent = ''; try { skillContent = fs.readFileSync( path.join(__dirname, '..', 'skills', 'unslop', 'SKILL.md'), 'utf8' ); } catch (e) { /* try activation rule next */ } // Fallback: try the activation rule file (lighter weight than full SKILL.md) let activationRule = ''; if (!skillContent) { try { activationRule = fs.readFileSync( path.join(__dirname, '..', 'rules', 'unslop-activate.md'), 'utf8' ).trim(); } catch (e) { /* will use hardcoded fallback */ } } let output; if (skillContent) { const body = skillContent.replace(/^---[\s\S]*?---\s*/, ''); // Filter intensity table and examples to the active level const filtered = body.split('\n').reduce((acc, line) => { const tableRowMatch = line.match(/^\|\s*\*\*(\S+?)\*\*\s*\|/); if (tableRowMatch) { if (tableRowMatch[1] === mode) { acc.push(line); } return acc; } const exampleMatch = line.match(/^- (\S+?):\s/); if (exampleMatch) { if (exampleMatch[1] === mode) { acc.push(line); } return acc; } acc.push(line); return acc; }, []); output = 'UNSLOP MODE ACTIVE — level: ' + mode + '\n\n' + filtered.join('\n'); } else if (activationRule) { output = 'UNSLOP MODE ACTIVE — level: ' + mode + '\n\n' + activationRule; } else { output = 'UNSLOP MODE ACTIVE — level: ' + mode + '\n\n' + 'Write like a careful human. All technical substance stays exact. Only AI-slop dies.\n\n' + '## Persistence\n\n' + 'ACTIVE EVERY RESPONSE. No revert after many turns. No drift back into AI-template English.\n' + 'Off only: "stop unslop" / "normal mode".\n\n' + 'Current level: **' + mode + '**. Switch: `/unslop subtle|balanced|full|voice-match|anti-detector`.\n\n' + '## Rules\n\n' + 'Drop: sycophancy ("great question", "I\'d be happy to"), stock vocab (delve/tapestry/testament/seamless/holistic/leverage-as-filler), ' + 'hedging stacks ("it\'s important to note that"), tricolon padding, em-dash pileups, performative balance, tidy five-paragraph shapes.\n\n' + 'Keep: technical terms exact, code unchanged, real uncertainty when honest.\n' + 'Engineer burstiness: mix short and long sentences deliberately.\n\n' + 'Pattern: [concrete observation]. [why]. [what to do next].\n\n' + '## Auto-Clarity\n\n' + 'Drop unslop style for: security warnings, irreversible actions, legal/medical/financial precision, user confused. Resume after.\n\n' + '## Boundaries\n\n' + 'Code/commits/PRs: write normal. "stop unslop" or "normal mode": revert. Level persists until changed or session ends.'; } // Detect missing statusline config — nudge Claude to help set it up try { let hasStatusline = false; if (fs.existsSync(settingsPath)) { const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8')); if (settings.statusLine) { hasStatusline = true; } } if (!hasStatusline) { const isWindows = process.platform === 'win32'; const scriptName = isWindows ? 'unslop-statusline.ps1' : 'unslop-statusline.sh'; const scriptPath = path.join(__dirname, scriptName); const command = isWindows ? `powershell -ExecutionPolicy Bypass -File "${scriptPath}"` : `bash "${scriptPath}"`; const statusLineSnippet = '"statusLine": { "type": "command", "command": ' + JSON.stringify(command) + ' }'; output += "\n\n" + "STATUSLINE SETUP NEEDED: The unslop plugin includes a statusline badge showing active mode " + "(e.g. [unslop], [unslop:full]). It is not configured yet. " + "To enable, add this to " + path.join(claudeDir, 'settings.json') + ": " + statusLineSnippet + " " + "Proactively offer to set this up for the user on first interaction."; } } catch (e) { // Silent fail — don't block session start over statusline detection } process.stdout.write(output); - hooks/unslop-config.jsGitHub
Read the script
#!/usr/bin/env node // unslop — shared configuration resolver // // Resolution order for default mode: // 1. UNSLOP_DEFAULT_MODE environment variable // 2. Config file defaultMode field: // - $XDG_CONFIG_HOME/unslop/config.json (any platform, if set) // - ~/.config/unslop/config.json (macOS / Linux fallback) // - %APPDATA%\unslop\config.json (Windows fallback) // 3. 'balanced' const fs = require('fs'); const path = require('path'); const os = require('os'); const VALID_MODES = [ 'off', 'subtle', 'balanced', 'full', 'voice-match', 'anti-detector', 'commit', 'review' ]; function getConfigDir() { if (process.env.XDG_CONFIG_HOME) { return path.join(process.env.XDG_CONFIG_HOME, 'unslop'); } if (process.platform === 'win32') { return path.join( process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'), 'unslop' ); } return path.join(os.homedir(), '.config', 'unslop'); } function getConfigPath() { return path.join(getConfigDir(), 'config.json'); } function getDefaultMode() { const envMode = process.env.UNSLOP_DEFAULT_MODE; if (envMode && VALID_MODES.includes(envMode.toLowerCase())) { return envMode.toLowerCase(); } try { const configPath = getConfigPath(); const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); if (config.defaultMode && VALID_MODES.includes(config.defaultMode.toLowerCase())) { return config.defaultMode.toLowerCase(); } } catch (e) { // Config file doesn't exist or is invalid } return 'balanced'; } // Symlink-safe flag file write. // Refuses symlinks at the target file and at the immediate parent directory, // uses O_NOFOLLOW where available, writes atomically via temp + rename with // 0600 permissions. Protects against local attackers replacing the predictable // flag path with a symlink to clobber other files. function safeWriteFlag(flagPath, content) { try { const flagDir = path.dirname(flagPath); fs.mkdirSync(flagDir, { recursive: true }); try { if (fs.lstatSync(flagDir).isSymbolicLink()) return; } catch (e) { return; } try { if (fs.lstatSync(flagPath).isSymbolicLink()) return; } catch (e) { if (e.code !== 'ENOENT') return; } const tempPath = path.join(flagDir, `.unslop-active.${process.pid}.${Date.now()}`); const O_NOFOLLOW = typeof fs.constants.O_NOFOLLOW === 'number' ? fs.constants.O_NOFOLLOW : 0; const flags = fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL | O_NOFOLLOW; let fd; try { fd = fs.openSync(tempPath, flags, 0o600); fs.writeSync(fd, String(content)); try { fs.fchmodSync(fd, 0o600); } catch (e) { /* best-effort on Windows */ } } finally { if (fd !== undefined) fs.closeSync(fd); } fs.renameSync(tempPath, flagPath); } catch (e) { // Silent fail — flag is best-effort } } // Symlink-safe, size-capped, whitelist-validated flag file read. // Returns null on any anomaly — never inject untrusted bytes into model context. const MAX_FLAG_BYTES = 64; function readFlag(flagPath) { try { let st; try { st = fs.lstatSync(flagPath); } catch (e) { return null; } if (st.isSymbolicLink() || !st.isFile()) return null; if (st.size > MAX_FLAG_BYTES) return null; const O_NOFOLLOW = typeof fs.constants.O_NOFOLLOW === 'number' ? fs.constants.O_NOFOLLOW : 0; const flags = fs.constants.O_RDONLY | O_NOFOLLOW; let fd; let out; try { fd = fs.openSync(flagPath, flags); const buf = Buffer.alloc(MAX_FLAG_BYTES); const n = fs.readSync(fd, buf, 0, MAX_FLAG_BYTES, 0); out = buf.slice(0, n).toString('utf8'); } finally { if (fd !== undefined) fs.closeSync(fd); } const raw = out.trim().toLowerCase(); if (!VALID_MODES.includes(raw)) return null; return raw; } catch (e) { return null; } } function getFlagPath() { const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'); return path.join(claudeDir, '.unslop-active'); } // Persona-drift reinforcement counter. Tracks how many user turns have // passed in this session while unslop has been active. RMTBench measures // >30% persona degradation after 8–12 turns; HorizonBench (arXiv // 2604.17283, Apr 2026) benchmarks preference evolution over time. We use // the counter to re-emit a shorter reinforcement banner at predetermined // drift-risk checkpoints rather than every turn (which would get tuned out). function getTurnCounterPath() { const claudeDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude'); return path.join(claudeDir, '.unslop-turn-count'); } // Read the counter. Same symlink-safe / size-capped discipline as readFlag. function readTurnCount(counterPath) { try { let st; try { st = fs.lstatSync(counterPath); } catch (e) { return 0; } if (st.isSymbolicLink() || !st.isFile()) return 0; if (st.size > 32) return 0; const O_NOFOLLOW = typeof fs.constants.O_NOFOLLOW === 'number' ? fs.constants.O_NOFOLLOW : 0; const flags = fs.constants.O_RDONLY | O_NOFOLLOW; let fd, raw; try { fd = fs.openSync(counterPath, flags); const buf = Buffer.alloc(32); const n = fs.readSync(fd, buf, 0, 32, 0); raw = buf.slice(0, n).toString('utf8').trim(); } finally { if (fd !== undefined) fs.closeSync(fd); } const n = parseInt(raw, 10); if (!Number.isFinite(n) || n < 0 || n > 1_000_000) return 0; return n; } catch (e) { return 0; } } // Symlink-safe atomic-rename write of the counter. Uses the same pattern as // safeWriteFlag to resist local-attacker symlink games. function writeTurnCount(counterPath, n) { try { const dir = path.dirname(counterPath); fs.mkdirSync(dir, { recursive: true }); try { if (fs.lstatSync(dir).isSymbolicLink()) return; } catch (e) { return; } try { if (fs.lstatSy - hooks/unslop-mode-tracker.jsGitHub
- hooks/unslop-statusline.ps1GitHub
- hooks/unslop-statusline.shGitHub
All 9 scripts are listed above. The source is inlined for 6 of them, starting with whatever hooks.json actually runs. See all of them in the repo.
Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.
Make AI output sound human. Strips AI-isms (sycophancy, stock vocab, hedging stacks, em-dash pileups), preserves code/URLs/headings. Plugin for Claude Code, Cursor, Windsurf, Codex, Cline, Copilot, Gemini.
Repo: MohamedAbdallah-14/unslop

