{"slug": "evaluate-the-profanity-used-working-with-codex-and-claude-code", "title": "Evaluate the profanity used working with Codex and Claude Code", "summary": "Marcel Petrick released Agentic Swear Jar, a private, local, zero-AI dashboard that analyzes profanity in Claude Code and Codex CLI histories, with no prompts uploaded and no runtime dependencies beyond Linux and Python 3.10. The tool parses history files, generates a standalone report.html, and provides metrics such as matches per 100 prompts and per 1,000 words, intensity counts, and trends, while excluding raw prompts and identifiers.", "body_md": "**A private, local, zero-AI dashboard for the spicy parts of your Claude Code and Codex CLI history.**\n\n`one command`\n\n· `two coding agents`\n\n· `zero prompts uploaded`\n\nBuilding software with an agent is still building software. Tests fail, tools loop, the same bug returns wearing a small fake moustache, and occasionally the most precise technical response is a four-letter word.\n\nThat is not a conduct problem. **A coding harness is a machine. It has no feelings to hurt.** Your\nlanguage is simply another trace of the work: a tiny pressure gauge for friction, intensity, late\nnights, stubborn projects, and the glorious moment the fix finally lands.\n\nAgentic Swear Jar turns that trace into something worth smiling at. It compares Claude Code with Codex CLI, finds trends, ranks vocabulary, and produces a polished standalone report. It does all of this locally with ordinary parsing, Unicode-aware tokenization, dictionary lookups, and arithmetic. No model judges the language. No API receives the prompts. No raw prompt text goes into the report.\n\n**Author: Marcel Petrick mail@marcelpetrick.it**\n\n**License: GPLv3 or later. See LICENSE.**\n\n**Note: projected is generated with AI.**\n\nRequirements: Linux and Python 3.10 or newer. There are no runtime dependencies.\n\n```\n./swear-stats --open\n```\n\nThat reads both default histories:\n\n| Tool | Default input | Prompt field |\n|---|---|---|\n| Claude Code | `~/.claude/history.jsonl` |\n`display` |\n| Codex CLI | `~/.codex/history.jsonl` |\n`text` |\n\nThe output is `report.html`\n\n, a self-contained file that works offline. If a history is missing, the\ntool warns and continues with the one it can find. Use `--strict-inputs`\n\nwhen both are required.\n\nWant aggregate JSON as well?\n\n```\n./swear-stats --json report.json --output report.html\n```\n\nWant a particular era of your life?\n\n```\n./swear-stats --since 2026-01-01 --until 2026-06-30 --open\n```\n\nThe dates are inclusive and interpreted in the machine's local timezone.\n\nThe report has combined, Claude Code, and Codex CLI views. Each includes:\n\n- prompt count, word count, total matches, and prompts containing at least one match;\n- matches per 100 prompts and per 1,000 words, so tools with different usage volumes compare fairly;\n- daily and monthly trends, local hour-of-day activity, and weekday patterns;\n- canonical vocabulary rankings (\n`fucked`\n\n,`fucking`\n\n, and`fucks`\n\nbecome`fuck`\n\n); - mild, moderate, and strong intensity counts;\n- match rates by prompt-length bucket;\n- longest clean and spicy prompt streaks;\n- distinct session and Claude project counts, without exposing their identifiers; and\n- malformed-record diagnostics, because JSONL occasionally has a bad day too.\n\nThe dashboard intentionally does **not** contain prompts, snippets, session IDs, project names, or\nproject paths.\n\nThe analyzer streams each JSONL file one line at a time. Every prompt is tokenized once with a compiled Unicode-aware regular expression. Normalized tokens are checked against an in-memory hash map, making lookup effectively constant-time. Only counters, date buckets, and tiny timestamp/event tuples survive analysis.\n\nFor an input containing *n* characters and *w* words, the main scan is **O(n + w)**. Memory use is\nindependent of prompt content and grows only with the number of dates, sessions, and aggregate\nlabels. It does not shell out to `grep`\n\nor `jq`\n\n, so multiline JSON strings, Unicode, and malformed\nrecords are handled consistently in one pass.\n\nMatching is case-insensitive and uses whole tokens. This avoids classic substring mistakes such as\nmatching `ass`\n\ninside `class`\n\nor `hell`\n\ninside `shell`\n\n. Underscores act as separators, which is\nuseful for identifiers such as `what_the_hell`\n\n.\n\nThere is no universal database of swear words. Meaning depends on geography, context, reclaimed\nlanguage, and personal taste; aggressive blocklists also run into the\n[Scunthorpe problem](https://en.wikipedia.org/wiki/Scunthorpe_problem). This project therefore ships\na small, transparent English list aimed at ordinary expletives—not hate-speech moderation. It has\nfour tab-separated columns:\n\n```\n# variant    canonical    category      severity\nfucking      fuck         expletive     3\nwtf          fuck         abbreviation  2\n```\n\nEdit [ swearstats/data/en.tsv](/marcelpetrick/AgenticSwearJar/blob/master/swearstats/data/en.tsv), or layer personal terms on top without\nchanging the repository:\n\n```\n./swear-stats --lexicon my-words.tsv\n```\n\nLater files override earlier variants. To discard the bundled English list entirely:\n\n```\n./swear-stats --replace-lexicon --lexicon my-words.tsv\n```\n\nUseful public datasets do exist. The MIT-licensed\n[ @dsojevic/profanity-list](https://github.com/dsojevic/profanity-list) adds severity, exceptions,\nand tags;\n\n[rates terms by how likely they are to be profane; and](https://github.com/words/cuss)\n\n`cuss`\n\n[LDNOOBW](https://github.com/LDNOOBW/List-of-Dirty-Naughty-Obscene-and-Otherwise-Bad-Words)covers many languages. They are good raw material, but importing a large list blindly will change the meaning of the statistics and increase false positives. A personal list you understand is usually more honest.\n\n```\nusage: swear-stats [-h] [--claude-history PATH] [--codex-history PATH]\n                   [--lexicon TSV] [--replace-lexicon]\n                   [--since YYYY-MM-DD] [--until YYYY-MM-DD]\n                   [-o PATH] [--json PATH] [--redact-terms]\n                   [--open] [--strict-inputs]\n```\n\nExamples:\n\n```\n# Nonstandard config directories\n./swear-stats \\\n  --claude-history /mnt/private/claude/history.jsonl \\\n  --codex-history /mnt/private/codex/history.jsonl\n\n# Machine-readable aggregates only in addition to HTML\n./swear-stats --json build/stats.json --output build/dashboard.html\n\n# Install an isolated CLI for development\npython3 -m venv .venv\n.venv/bin/pip install -e .\n.venv/bin/swear-stats --open\n```\n\nThis project analyzes the short, global prompt histories rather than full session transcripts, so each user input is counted once and tool outputs are excluded.\n\n- Anthropic documents\n`~/.claude/history.jsonl`\n\nas every typed prompt with its timestamp and project path, retained until deletion. Full transcripts live below`~/.claude/projects/`\n\nand are subject to`cleanupPeriodDays`\n\n(30 days by default). See[Explore the](https://code.claude.com/docs/en/claude-directory).`.claude`\n\ndirectory - OpenAI's Codex source defines\n`~/.codex/history.jsonl`\n\nas append-only JSONL records shaped like`{\"session_id\":\"…\",\"ts\":1234567890,\"text\":\"…\"}`\n\n. See the[Codex message-history implementation](https://github.com/openai/codex/blob/main/codex-rs/message-history/src/lib.rs).\n\nBoth formats are implementation details that may evolve. Unknown or malformed records are skipped and reported rather than crashing the whole run.\n\nThe script reads plaintext histories, so treat it with the same access controls as the coding tools\nthemselves. Generated `report.html`\n\nand `report.json`\n\nare ignored by Git by default. Although they\ncontain aggregates only, the canonical-term ranking is still personal information—share it because\nit is funny, not by accident.\n\nTo stop future local prompt history, consult each tool's current settings. Anthropic documents\n`CLAUDE_CODE_SKIP_PROMPT_HISTORY`\n\n; Codex supports `[history] persistence = \"none\"`\n\nin its config.\nChanging those settings also affects recall and resume behavior, so read the upstream documentation\nbefore flipping the switch.\n\nRun the same local quality gate used before every commit:\n\n```\n./localPipeline.sh\n```\n\nThe application has no runtime dependencies. The development pipeline expects `shellcheck`\n\n, `ruff`\n\n,\nand `mypy`\n\non `PATH`\n\n; it runs the complete test suite, shell and Python linting, formatting checks,\nstrict type checking, and Git's whitespace validation, stopping at the first failure.\n\nThe test suite covers both history shapes, canonical variants, Unicode-aware whole-word behavior, date filtering, malformed records, combined statistics, and the promise that raw prompt text and project paths never enter the HTML.\n\nThe repository includes a reproducible public build based on the real Claude Code and Codex CLI histories:\n\n```\n./buildPublicArtifacts.sh\n```\n\nIt requires Chromium and `img2pdf`\n\n, then creates and updates:\n\n`public/report.html`\n\n— the interactive, redacted dashboard;`public/report.json`\n\n— aggregate data behind every published number;`public/linkedin-carousel.html`\n\n— four-slide carousel source;`public/slides/`\n\n— four 1080×1080 RGB screenshots; and`public/agentic-swear-jar-linkedin.pdf`\n\n— the square, four-page LinkedIn document.\n\nThe complete `public/`\n\ndirectory is generated locally and ignored by Git. Rebuilding it never stages\npersonal statistics or publication files by accident.\n\nThe public build uses `--redact-terms`\n\n: observed vocabulary retains its first character for visual\nrhythm, while every remaining character becomes a solid marker block. The carousel builder refuses\nunredacted JSON. Raw prompts, project paths, session IDs, and uncensored observed terms are not\npublished.\n\n- This is exact lexical matching, not contextual language understanding.\n- It will miss creative punctuation, deliberate obfuscation, and novel spellings.\n- It can count a quoted swear word or code identifier even when the prompt is discussing the word.\n- Severity is editorial metadata, not science.\n- Missing or disabled history cannot be reconstructed.\n- Counts answer “what matched this list?”—not “was this prompt offensive?” Those are very different questions, and this project only claims the first.\n\n**May your tests be green and your vocabulary statistically significant.**\n\nCopyright © 2026 Marcel Petrick. Licensed under the\n[GNU General Public License v3.0 only](/marcelpetrick/AgenticSwearJar/blob/master/LICENSE).", "url": "https://wpnews.pro/news/evaluate-the-profanity-used-working-with-codex-and-claude-code", "canonical_source": "https://github.com/marcelpetrick/AgenticSwearJar", "published_at": "2026-08-12 09:48:38+00:00", "updated_at": "2026-08-12 10:12:15.689316+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Marcel Petrick", "Claude Code", "Codex CLI", "Agentic Swear Jar"], "alternates": {"html": "https://wpnews.pro/news/evaluate-the-profanity-used-working-with-codex-and-claude-code", "markdown": "https://wpnews.pro/news/evaluate-the-profanity-used-working-with-codex-and-claude-code.md", "text": "https://wpnews.pro/news/evaluate-the-profanity-used-working-with-codex-and-claude-code.txt", "jsonld": "https://wpnews.pro/news/evaluate-the-profanity-used-working-with-codex-and-claude-code.jsonld"}}