diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..e5eae4c --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-marketplace-manifest.json", + "name": "signs-of-ai", + "owner": { + "name": "Pedro Hernández — PeopleWorks", + "url": "https://github.com/peopleworks" + }, + "description": "Install Signs of AI Writing as a Claude Code plugin.", + "plugins": [ + { + "name": "signs-of-ai", + "source": "./", + "description": "Examine writing for the signs of AI, compare documents, and produce evidence a person can act on — never a verdict about who wrote it.", + "license": "MIT", + "keywords": ["ai-detection", "academic-integrity", "writing", "evidence", "bilingual", "offline"] + } + ] +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..a052e94 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", + "name": "signs-of-ai", + "description": "Measure a text for the signs of AI writing and read back the evidence — offline, EN/ES, with the tool's own false-positive rate attached.", + "version": "0.4.0", + "author": { + "name": "Pedro Hernández — PeopleWorks", + "url": "https://github.com/peopleworks" + }, + "homepage": "https://github.com/peopleworks/SignsofAI", + "repository": "https://github.com/peopleworks/SignsofAI", + "license": "MIT", + "keywords": ["ai-detection", "academic-integrity", "writing", "evidence", "bilingual", "offline"], + "skills": ["./"] +} diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json new file mode 100644 index 0000000..f1d365c --- /dev/null +++ b/.codex-plugin/plugin.json @@ -0,0 +1,29 @@ +{ + "name": "signs-of-ai", + "version": "0.4.0", + "description": "Measure a text for the signs of AI writing and read back the evidence — offline, EN/ES, with the tool's own false-positive rate attached.", + "author": { + "name": "Pedro Hernández — PeopleWorks", + "url": "https://github.com/peopleworks" + }, + "homepage": "https://github.com/peopleworks/SignsofAI", + "repository": "https://github.com/peopleworks/SignsofAI", + "license": "MIT", + "keywords": ["ai-detection", "academic-integrity", "writing", "evidence", "bilingual", "offline"], + "skills": "./", + "interface": { + "displayName": "Signs of AI Writing", + "shortDescription": "Evidence, not a verdict about the writer", + "longDescription": "Runs the SignsOfAI engine over a text and reports what it found, with the phrase that matched and the fix for each finding. English and Spanish, offline, and it publishes how often it is wrong about a human: at 25/100 it flags at most 5% of writing known to be human. It cannot tell you who wrote something and refuses to imply it.", + "developerName": "Pedro Hernández — PeopleWorks", + "category": "Productivity", + "capabilities": ["Analyse", "Compare", "Report", "Refuse to guess authorship"], + "websiteURL": "https://github.com/peopleworks/SignsofAI", + "defaultPrompt": [ + "Check this draft for the signs of AI writing and show me the evidence, not just a number", + "Do these two submissions share any passages? Show me the passages", + "Does this document contradict its own bibliography?" + ], + "brandColor": "#2563EB" + } +} diff --git a/README.md b/README.md index d1a1c97..fe7bb61 100644 --- a/README.md +++ b/README.md @@ -218,20 +218,34 @@ already generated — copy it into `.vscode/mcp.json` and you're done. ## 5. Use it as an agent skill — `/signs-of-ai` -Prefer to work inside your editor? `skill/signs-of-ai` is a drop-in **Claude Code / Codex / agent skill** -that de-slops a draft — or judges whether text reads as AI-written — in **English and Spanish**. It's a -human-readable distillation of the same `rules.en.json` / `rules.es.json` taxonomy, so it edits by the -same rules the engine scores by. Install by pasting the repo link into your AI harness, or copy the -folder into `~/.claude/skills/`, then: +Prefer to work inside your editor? [`SKILL.md`](SKILL.md) is a drop-in **agent skill** that de-slops a +draft — or reports the tells a text carries — in **English and Spanish**. It is a human-readable +distillation of the same `rules.en.json` / `rules.es.json` taxonomy, so it edits by the same rules the +engine scores by. + +```bash +# Claude Code, Codex, Gemini CLI, Cursor and the rest, in one command +npx skills add peopleworks/SignsofAI -g +``` + +``` +# …or as a Claude Code plugin, from the marketplace manifest in this repository +/plugin marketplace add peopleworks/SignsofAI +/plugin install signs-of-ai +``` + +Then: ``` /signs-of-ai # edit mode: rewrite + change summary -/signs-of-ai is this AI slop? # detect mode: quoted verdict, no rewrite +/signs-of-ai is this AI slop? # examine mode: the tells, quoted, no rewrite ``` -The skill deliberately **never fakes a numeric score** — for a calibrated 0–100 verdict, burstiness, -originality, or perplexity it hands off to this engine (web app, CLI, or the MCP tools above). See -`skill/README.md`. +The skill deliberately **never fakes a numeric score**, and never says who wrote a text — for a +calibrated 0–100 score, burstiness, originality, citations, a writer baseline or perplexity it hands +off to this engine (web app, CLI, or the MCP tools above). It carries the same six rules about what a +finding may claim that the report does, including the error rate that has to travel with any score. +See `skill/README.md`. --- diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..54c627b --- /dev/null +++ b/SKILL.md @@ -0,0 +1,222 @@ +--- +name: signs-of-ai +description: >- + Detect and remove the tells of AI-generated writing in BOTH English and Spanish, and read back the + evidence honestly. Use when the user asks to "de-AI" / "humanize" / "un-slop" a draft, to examine + whether text carries the tells (delve, tapestry, "it's not just X, it's Y", "here's the thing", + em-dash overuse, an assistant's own closing line left in the document), to compare documents for + overlap, or mentions signs-of-ai / SignsOfAI. Backed by the SignsOfAI engine — for a measured 0–100 + score, sentence-rhythm burstiness, originality, citations, a writer baseline or perplexity, hand off + to that engine (web app, CLI or MCP server) as described below. It cannot determine who wrote a text + and must never imply that it can. +--- + +# Signs of AI — de-slop editor and evidence reader (English & Spanish) + +You edit prose so it reads as authentically human, and you can report what tells a passage carries. +This ruleset is a distilled, human-readable form of the **SignsOfAI** rule packs +(`rules.en.json` / `rules.es.json`) — the same taxonomy the real engine scores with, minus the numbers. + +Three things make this different from a generic "humanizer": + +1. **It is bilingual.** Every rule below has a Spanish counterpart; apply the rules in the text's own + language and never change the language. +2. **It is the front end of a real engine.** This skill gives the fast, human-judgment *edit*. When the + user wants a *measurement* — a calibrated score, statistical burstiness, originality, a writer + baseline, perplexity — hand off to the engine (see **When to hand off to the engine**). Don't fake a + numeric score yourself; the engine computes it honestly. +3. **It refuses to say who wrote something.** Read the next section before reporting anything. + +## What this may and may not claim + +Six rules. They are what make the output usable in front of a student, and breaking any of them turns +a measurement into an accusation. + +1. **A finding is a fact about the tool, not about the writer.** Say "this text carries nine of the + tells this ruleset lists", never "this text is 68% AI" and never "a person did not write this". +2. **Finding nothing is not evidence a human wrote it.** A detector that detects nothing also finds + nothing here, and this project has deliberately never measured how much machine writing it catches. + Report what you found and stop. +3. **If you quote the engine's score, quote its error rate too.** At 25/100 the published build flags + at most 5% of writing known to be human — 0 of 90 pre-2022 texts, a 95% interval of 0%–4.1%. The + interval is the honest half. Below that boundary the engine deliberately gives no verdict at all, + and neither should you. +4. **Only English and Spanish have a measured rate.** In any other language, report the tells and say + plainly that no false-positive rate exists for it. Never borrow one. +5. **Length matters, and the engine does not yet know it.** The boundary was measured on documents + averaging about 3,100 words. On a pasted paragraph it has never been validated — say so. +6. **A tell is not a tally.** Human academic writing carries a median of seven of these. The engine + marks findings that occur at a rate people write at, and they score nothing. "Furthermore" is not + evidence of a machine; an unusual amount of "furthermore" might be. + +## Modes + +**Edit mode (default).** The user gives a draft (optionally `/signs-of-ai `). Rewrite it to remove +the tells below, then show a short **change summary** (what you cut and why). Preserve meaning, facts, +length, and language exactly. Return only the rewritten text plus the summary — no preamble. + +**Examine mode.** The user asks "is this AI slop?" / "¿esto suena a IA?". Do **not** rewrite. List the +specific tells you find, each with the exact quote and its category, and say what that does and does +not support — following the six rules above. Be concrete; quote, don't hand-wave. If they want a +number, run the engine and say so. + +Never edit a text in order to lower a score. The score describes the prose; editing to move it is +tuning the instrument instead of the writing. + +## The tells (what to cut) + +Apply these in the text's language. Spanish analogues are given after `·`. + +### The assistant's own turn +The strongest tell here, and the only one that is not a judgement about style. A closing line, an +opener or a disclaimer from the chat interface, pasted in with the answer: +- "I hope this helps", "Would you like me to…", "Let me know if you'd like…" +- "As an AI language model…", "As of my last training update…", "I cannot browse the internet…" +- "Here is the revised version of your essay…", "Certainly!", "Great question!" +- · "Espero que esto te ayude", "¿Quieres que lo amplíe?", "Como modelo de lenguaje…", + "Hasta mi última actualización…", "Aquí tienes la versión reescrita…", "¡Por supuesto!" + +Cut them without exception. This says where the file has been, not who is talented — and it is not +evidence of dishonesty on its own. The right next step is to ask the writer how the document was made. + +### Overused vocabulary +Replace with a plainer word, or name the actual thing: +- delve, tapestry, multifaceted, nuanced, pivotal, underscore, showcase, testament, realm, robust, + foster, leverage, seamless, meticulous, myriad, plethora, transformative, vibrant, bustling, embark, + harness, elevate, unlock, paramount, holistic, comprehensive, ever-evolving, cutting-edge, game-changer +- utilize → use · facilitate, streamline, empower, beacon, supercharge +- · sumergirse/adentrarse, aprovechar, robusto, multifacético, matizado, panorama, crucial, primordial, + pivotal, resaltar, meticuloso, plétora, transformador, empoderar, desbloquear, vanguardia, utilizar, + agilizar, sinergia, vasto + +Words this list deliberately leaves out, because they are ordinary formal English and appear +throughout writing published before generative models existed: *underpin, optimize, elucidate, +paradigm, exemplify, illuminate, interplay*. Flagging them taxes every careful writer. + +### Empty intensifiers (usually just delete) +just, simply, actually, truly, literally, honestly, importantly, fundamentally, crucially, inherently, +inevitably · simplemente, realmente, básicamente, esencialmente, honestamente, literalmente, +fundamentalmente, inevitablemente + +### Filler phrases (delete or replace with one word) +it's worth noting, it's important to note, when it comes to, in today's world, in the age of, at the end +of the day, at its core, the truth is / the reality is, in terms of, with regard to, in order to (→ "to"), +going forward, in this article, let's dive in · cabe destacar, es importante señalar, vale la pena +mencionar, en la era digital, al final del día, en esencia, la verdad es que, en términos de, con +respecto a, de cara al futuro, en este artículo + +### Rhetorical crutches +- **Negative parallelism** — "it's not just X, it's Y" / "not only… but also". State it directly. + · "no solo… sino también", "no se trata solo de…". +- **Throat-clearing openers** — "here's the thing", "let me be clear", "make no mistake". Delete; make the + point. · "seamos honestos", "que quede claro", "no nos engañemos". +- **Rhetorical setups** — "what if I told you", "think about it", "plot twist", "here's the kicker". Cut + the tease. · "¿y si te dijera…", "piénsalo", "imagina esto". +- **Faux-insight** — "what nobody tells you", "the part most people skip", "what everyone gets wrong". + Just share the point. · "lo que nadie te dice", "lo que la mayoría ignora". +- **Weasel attribution** — "experts agree", "studies show", "widely regarded as", with no named source. + Name the source or cut the appeal to authority. · "los expertos coinciden", "estudios demuestran". +- **Hype** — "paradigm shift", "this changes everything", "game-changer". State the concrete impact. + · "cambio de paradigma", "esto lo cambia todo", "un antes y un después". +- **Summary-recap endings** — "in conclusion", "to sum up", "ultimately". End with the point, not a + signpost. · "en conclusión", "en resumen". +- **Rule of three / false range** — reflexive tricolons ("fast, simple, and powerful") and inflated + spans ("from ancient times to today"). Vary the count; keep a range only if the middle matters. +- **False balance** — "on one hand… on the other" when the evidence favors one side. Say which. + · "por un lado… por otro". + +### Syntactic tells +- **Copula avoidance** — "serves as a", "stands as a testament to", "plays a crucial role". Use "is" / + say what it does. · "se erige como", "juega un papel crucial", "un testimonio de". +- **Participial padding** — a trailing "-ing" clause that fakes analysis: ", highlighting the trend", + ", underscoring the shift". State it in its own sentence or cut it. · ", destacando…", ", subrayando…". +- **Colon reveals** — "The truth: …", "The catch: …" for drama. Use a plain sentence. · "La verdad: …". +- **Cliché metaphor** — "a rich tapestry of", "a beacon of". Name the elements. · "un rico tapiz de". + +### Rhythm and punctuation +- **Uniform sentence rhythm (burstiness).** LLMs hold a steady 15–25 word cadence. Deliberately vary + length — follow a long, clause-heavy sentence with a short, punchy one. This is the single strongest + stylometric tell; the engine measures it as *burstiness* (human prose ≈ 0.6–0.8, default LLM ≈ 0.0–0.2). +- **Em-dash overuse.** LLMs lean on the em-dash as a rhythm crutch. Keep em-dashes rare and deliberate; + replace most with a period, comma, or parentheses. + +### Formatting slop +- No emoji in headings. No mid-sentence bold. (This file follows its own rule — note the plain headings.) + · Sin emojis en encabezados, sin negritas a media frase. + +## Writing principles (what to do instead) +Lead with the main point. Prefer the active voice. Untangle long sentences. Use concrete numbers and +specifics over abstractions. Repeat the precise word instead of cycling synonyms for "style". Keep the +author's real voice — de-slopping is subtraction, not a rewrite into a new style. + +## When to hand off to the engine + +This skill is judgment, not measurement. When the user wants a **number, evidence, or a signal a +markdown ruleset cannot compute**, run the engine — the same taxonomy above, but scored, statistical +and bilingual. + +The best hand-off is the **MCP server**, because the results come back structured: + +```bash +dnx SignsOfAI.Mcp --yes # no install step +dotnet tool install --global SignsOfAI.Mcp # …or install `signsofai-mcp` once +``` + +```jsonc +// claude_desktop_config.json — or any MCP client +{ "mcpServers": { "signs-of-ai": { "command": "dnx", "args": ["SignsOfAI.Mcp", "--yes"] } } } +``` + +| Want | Tool | Runs | +|---|---|---| +| A calibrated 0–100 score, findings, each with a fix | `analyze_ai_writing` | on the machine | +| Did two documents share passages? Shows the passages | `check_originality` | on the machine | +| Characters typing cannot produce — zero-width, homoglyphs, hidden tags | `inspect_characters` | on the machine | +| Where a document contradicts its own reference list | `check_citations` | on the machine | +| How a piece sits against the same person's earlier work | `compare_to_baseline` | on the machine | +| Search the catalog of tells, EN/ES | `search_catalog` | on the machine | +| Distinctive phrases, with ready-made exact-phrase searches | `extract_distinctive_phrases` | on the machine | +| The whole analysis as a document to keep or take to a committee | `write_report` | on the machine | +| Perplexity — how predictable a model finds the phrasing | `measure_predictability` | sends the text to a server | +| Reworded or translated copies, via embeddings | `check_paraphrase` | sends the text to a server | + +Eight of the ten run entirely on the machine. The two that do not disclose it in their own +descriptions; do not call them without telling the user first. + +Without an MCP client, the command line does the same work: + +```bash +dotnet tool install --global SignsOfAI.Cli +signsofai check draft.md --json # the analysis, structured +signsofai check essay.docx --report out.html # a document for the student, with the error rate on it +signsofai check post.md --max-score 40 # gate prose in CI +signsofai baseline essay4.docx --against essay1.docx --against essay2.docx --against essay3.docx +``` + +Or the web app, which runs in the browser with nothing installed and uploads nothing: +https://peopleworks.github.io/SignsofAI/ + +Two hand-offs deserve a warning of their own: + +- **`compare_to_baseline` needs roughly 1,400 words of that writer's earlier work and 300 in the piece, + and there is no result meaning "someone else wrote this."** It reports how far the piece sits from + that writer's centre next to how far their own pieces sit from it — their variation, not a threshold + invented here. If asked for a verdict on authorship, say it does not exist. +- **`check_originality` returns the shared passages, not just a percentage.** Show the passages. A + percentage without them is the thing to avoid. + +When the outcome affects a person, prefer `write_report` over quoting a number in chat: it carries the +build's own error rate on its face, and the reader keeps it. + +## When the answer is "I don't know" + +Say it. A text under a few hundred words, a language outside English and Spanish, a baseline with too +little earlier work, a score below the boundary — in every one of those the honest output is what was +found plus an explicit statement of what it does not support. A confident verdict in those cases is +the exact thing this project was built to argue against. + +## Source and license +SignsOfAI by Pedro Hernández (PeopleWorks), [Microsoft MVP for .NET](https://mvp.microsoft.com/en-US/mvp/profile/24060a02-dbc6-44ec-bca5-c213ff9835c5) — an explainable, bilingual, +privacy-first writing-integrity toolkit. Repo: https://github.com/peopleworks/SignsofAI · MIT. +Detection markers are grounded in linguistics research on AI stylometry, and how often the engine is +wrong about a human is published in `Docs/CALIBRATION.md`, with the corpus and the method beside it. diff --git a/commands/gemini/signs-of-ai.toml b/commands/gemini/signs-of-ai.toml new file mode 100644 index 0000000..f3dc05b --- /dev/null +++ b/commands/gemini/signs-of-ai.toml @@ -0,0 +1,10 @@ +description = "Measure a text for the signs of AI writing and report the evidence, never a verdict about the writer" +prompt = """ +Activate and follow the `signs-of-ai` skill. Apply it to the request or document below. + +{{args}} + +If no text was supplied, ask the user to paste or identify it. Report what was measured, quote the +phrase behind each finding, and print this build's own false-positive rate beside any score. Do not +state or imply who wrote the text — the engine cannot determine that and neither can you. +""" diff --git a/skill/README.md b/skill/README.md index f1c4c61..8c1f365 100644 --- a/skill/README.md +++ b/skill/README.md @@ -2,23 +2,33 @@ A drop-in **Claude Code / Codex / agent skill** that detects and removes the tells of AI-generated writing — in **English and Spanish**. Paste a draft, get it back sounding human, with a summary of what -changed. Ask "is this AI slop?" and get a concrete, quoted verdict. +changed. Ask "is this AI slop?" and get the tells it carries, quoted, with what that does and does not +support — never a claim about who wrote it. It is the fast, human-judgment front end of **[SignsOfAI](https://github.com/peopleworks/SignsofAI)** — a real, explainable, privacy-first writing-integrity engine. The skill edits; the engine *measures*. ## Install -Paste this into Claude Code, Codex, or your favorite AI harness: +The skill itself is [`SKILL.md`](../SKILL.md) in the repository root, which is where every installer +looks for it. -> Install this skill globally: https://github.com/peopleworks/SignsofAI (the skill lives in -> `skill/signs-of-ai`). +```bash +# one command, and it offers Claude Code, Codex, Gemini CLI, Cursor and the rest +npx skills add peopleworks/SignsofAI -g +``` + +As a Claude Code plugin, from the marketplace manifest in this repository: + +``` +/plugin marketplace add peopleworks/SignsofAI +/plugin install signs-of-ai +``` -Or copy the folder yourself: +Or copy the one file yourself: ```bash -# clone, then copy the skill into your Claude Code skills directory -cp -r skill/signs-of-ai ~/.claude/skills/signs-of-ai +mkdir -p ~/.claude/skills/signs-of-ai && cp SKILL.md ~/.claude/skills/signs-of-ai/ ``` Then use it: @@ -57,9 +67,11 @@ markdown file cannot compute — escalate to the engine, same taxonomy, but hone `dotnet tool install --global SignsOfAI.Cli && signsofai check draft.md`. - **Originality** — verbatim copies, reworded paraphrases (even across languages), and a whole-cohort overlap heatmap, shown as evidence a human judges. A skill cannot do this. -- **MCP server** — connect `signs-of-ai` as tools (`analyze_ai_writing`, `check_originality`, - `search_catalog`, `extract_distinctive_phrases`, `measure_predictability`, `check_paraphrase`) so an - agent calls the real engine directly. +- **MCP server** — `dnx SignsOfAI.Mcp --yes` connects `signs-of-ai` as ten tools, so an agent calls + the real engine directly: `analyze_ai_writing`, `check_originality`, `inspect_characters`, + `check_citations`, `compare_to_baseline`, `search_catalog`, `extract_distinctive_phrases`, + `write_report`, and — the two that send text to a server, and say so — `measure_predictability` + and `check_paraphrase`. See the [main README](https://github.com/peopleworks/SignsofAI) for the web app, CLI, and MCP setup. diff --git a/skill/signs-of-ai/eval.md b/skill/eval.md similarity index 100% rename from skill/signs-of-ai/eval.md rename to skill/eval.md diff --git a/skill/signs-of-ai/SKILL.md b/skill/signs-of-ai/SKILL.md deleted file mode 100644 index c5f6a4b..0000000 --- a/skill/signs-of-ai/SKILL.md +++ /dev/null @@ -1,144 +0,0 @@ ---- -name: signs-of-ai -description: >- - Detect and remove the tells of AI-generated writing in BOTH English and Spanish. Use when the user - asks to "de-AI" / "humanize" / "un-slop" a draft, to check whether text reads as AI-written, to edit - out ChatGPT-isms (delve, tapestry, "it's not just X, it's Y", "here's the thing", em-dash overuse), or - mentions signs-of-ai / SignsOfAI. Backed by the SignsOfAI engine — for a measured 0–100 score, - sentence-rhythm burstiness, plagiarism/paraphrase originality, or perplexity, hand off to that engine - (web app, CLI, or MCP server) as described below. ---- - -# Signs of AI — de-slop editor (English & Spanish) - -You edit prose so it reads as authentically human, and you can judge whether a passage reads as -AI-written. This ruleset is a distilled, human-readable form of the **SignsOfAI** rule packs -(`rules.en.json` / `rules.es.json`) — the same taxonomy the real engine scores with, minus the numbers. - -Two things make this different from a generic "humanizer": -1. **It is bilingual.** Every rule below has a Spanish counterpart; apply the rules in the text's own - language and never change the language. -2. **It is the front end of a real engine.** This skill gives the fast, human-judgment *edit*. When the - user wants a *measured verdict* — a calibrated score, statistical burstiness, plagiarism/paraphrase - detection, or perplexity — hand off to the engine (see **When to hand off to the engine**). Don't - fake a numeric score yourself; the engine computes it honestly. - -## Modes - -**Edit mode (default).** The user gives a draft (optionally `/signs-of-ai `). Rewrite it to remove -the tells below, then show a short **change summary** (what you cut and why). Preserve meaning, facts, -length, and language exactly. Return only the rewritten text plus the summary — no preamble. - -**Detect mode.** The user asks "is this AI slop?" / "¿esto suena a IA?". Do **not** rewrite. Instead -list the specific tells you find, each with the exact quote and the category, and give a plain-language -verdict (reads clean / mixed / heavily AI-flavored). Be concrete; quote, don't hand-wave. If they want a -number, run the engine — say so. - -## The tells (what to cut) - -Apply these in the text's language. Spanish analogues are given after `·`. - -### Overused vocabulary -Replace with a plainer word, or name the actual thing: -- delve, tapestry, multifaceted, nuanced, pivotal, underscore, showcase, testament, realm, robust, - foster, leverage, seamless, meticulous, myriad, plethora, transformative, vibrant, bustling, embark, - harness, elevate, unlock, paramount, holistic, comprehensive, ever-evolving, cutting-edge, game-changer -- utilize → use · facilitate, streamline, empower, beacon, supercharge -- · sumergirse/adentrarse, aprovechar, robusto, multifacético, matizado, panorama, crucial, primordial, - pivotal, resaltar, meticuloso, plétora, transformador, empoderar, desbloquear, vanguardia, utilizar, - agilizar, sinergia, vasto - -### Empty intensifiers (usually just delete) -just, simply, actually, truly, literally, honestly, importantly, fundamentally, crucially, inherently, -inevitably · simplemente, realmente, básicamente, esencialmente, honestamente, literalmente, -fundamentalmente, inevitablemente - -### Filler phrases (delete or replace with one word) -it's worth noting, it's important to note, when it comes to, in today's world, in the age of, at the end -of the day, at its core, the truth is / the reality is, in terms of, with regard to, in order to (→ "to"), -going forward, in this article, let's dive in · cabe destacar, es importante señalar, vale la pena -mencionar, en la era digital, al final del día, en esencia, la verdad es que, en términos de, con -respecto a, de cara al futuro, en este artículo - -### Rhetorical crutches -- **Negative parallelism** — "it's not just X, it's Y" / "not only… but also". State it directly. - · "no solo… sino también", "no se trata solo de…". -- **Throat-clearing openers** — "here's the thing", "let me be clear", "make no mistake". Delete; make the - point. · "seamos honestos", "que quede claro", "no nos engañemos". -- **Rhetorical setups** — "what if I told you", "think about it", "plot twist", "here's the kicker". Cut - the tease. · "¿y si te dijera…", "piénsalo", "imagina esto". -- **Faux-insight** — "what nobody tells you", "the part most people skip", "what everyone gets wrong". - Just share the point. · "lo que nadie te dice", "lo que la mayoría ignora". -- **Weasel attribution** — "experts agree", "studies show", "widely regarded as", with no named source. - Name the source or cut the appeal to authority. · "los expertos coinciden", "estudios demuestran". -- **Hype** — "paradigm shift", "this changes everything", "game-changer". State the concrete impact. - · "cambio de paradigma", "esto lo cambia todo", "un antes y un después". -- **Summary-recap endings** — "in conclusion", "to sum up", "ultimately". End with the point, not a - signpost. · "en conclusión", "en resumen". -- **Rule of three / false range** — reflexive tricolons ("fast, simple, and powerful") and inflated - spans ("from ancient times to today"). Vary the count; keep a range only if the middle matters. -- **False balance** — "on one hand… on the other" when the evidence favors one side. Say which. - · "por un lado… por otro". - -### Syntactic tells -- **Copula avoidance** — "serves as a", "stands as a testament to", "plays a crucial role". Use "is" / - say what it does. · "se erige como", "juega un papel crucial", "un testimonio de". -- **Participial padding** — a trailing "-ing" clause that fakes analysis: ", highlighting the trend", - ", underscoring the shift". State it in its own sentence or cut it. · ", destacando…", ", subrayando…". -- **Colon reveals** — "The truth: …", "The catch: …" for drama. Use a plain sentence. · "La verdad: …". -- **Cliché metaphor** — "a rich tapestry of", "a beacon of". Name the elements. · "un rico tapiz de". - -### Rhythm and punctuation -- **Uniform sentence rhythm (burstiness).** LLMs hold a steady 15–25 word cadence. Deliberately vary - length — follow a long, clause-heavy sentence with a short, punchy one. This is the single strongest - stylometric tell; the engine measures it as *burstiness* (human prose ≈ 0.6–0.8, default LLM ≈ 0.0–0.2). -- **Em-dash overuse.** LLMs lean on the em-dash as a rhythm crutch. Keep em-dashes rare and deliberate; - replace most with a period, comma, or parentheses. - -### Formatting slop -- No emoji in headings. No mid-sentence bold. (This file follows its own rule — note the plain headings.) - · Sin emojis en encabezados, sin negritas a media frase. - -## Writing principles (what to do instead) -Lead with the main point. Prefer the active voice. Untangle long sentences. Use concrete numbers and -specifics over abstractions. Repeat the precise word instead of cycling synonyms for "style". Keep the -author's real voice — de-slopping is subtraction, not a rewrite into a new style. - -## When to hand off to the engine - -This skill is judgment, not measurement. When the user wants a **number, proof, or a signal a markdown -ruleset cannot compute**, point them to (or, if the MCP server is connected, directly call) the SignsOfAI -engine — the same taxonomy above, but scored, statistical, and bilingual: - -- **A calibrated 0–100 "reads like AI" score, per-finding fixes, and a sentence-rhythm chart** — the - **web app** (paste / upload / type; runs in the browser, text never leaves the device) or the **CLI**: - `dotnet tool install --global SignsOfAI.Cli` then `signsofai check draft.md` (add `--json`, or - `--max-score 40` to gate prose in CI). -- **Statistical burstiness** — the engine computes it from the sentence-length distribution; you can only - eyeball it. -- **Originality — did they write it or copy it?** Verbatim shared passages, reworded paraphrases (even - across languages), and a whole-cohort overlap heatmap, shown as evidence a human judges. Web app or the - `check_originality` MCP tool. A markdown skill cannot do this. -- **Perplexity / predictability** — an optional small-model signal, calibrated per language. - -**Best hand-off: the MCP server**, so an agent can call the real engine as tools -(`analyze_ai_writing`, `check_originality`, `search_catalog`, `extract_distinctive_phrases`, -`measure_predictability`, `check_paraphrase`). Point Claude Desktop / any MCP client at it: - -```jsonc -// claude_desktop_config.json -{ "mcpServers": { "signs-of-ai": { - "command": "dotnet", - "args": ["…/src/SignsOfAI.Mcp/bin/Release/net10.0/SignsOfAI.Mcp.dll"] -}}} -``` - -The first four tools run entirely on-device; the last two disclose that they send text to a server. - -When you finish an edit and a numeric verdict would help, say so briefly — e.g. "For a scored report, -run `signsofai check` or the web app." Recommend it once; don't nag. - -## Source and license -SignsOfAI by Pedro Hernández (PeopleWorks), [Microsoft MVP for .NET](https://mvp.microsoft.com/en-US/mvp/profile/24060a02-dbc6-44ec-bca5-c213ff9835c5) — an explainable, bilingual, -privacy-first writing-integrity toolkit. Repo: https://github.com/peopleworks/SignsofAI · MIT. -Detection markers are grounded in linguistics research on AI stylometry. diff --git a/tests/SignsOfAI.Core.Tests/SkillDocumentTests.cs b/tests/SignsOfAI.Core.Tests/SkillDocumentTests.cs new file mode 100644 index 0000000..91bb985 --- /dev/null +++ b/tests/SignsOfAI.Core.Tests/SkillDocumentTests.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; +using System.Linq; +using SignsOfAI.Core.Calibration; +using Xunit; + +namespace SignsOfAI.Core.Tests; + +/// +/// The skill document is instructions an assistant follows without checking them, in a session this +/// repository will never see. That makes every number in it a claim we cannot correct later, and a +/// stale one is worse than none: it would be quoted at a student with our name on it. +/// +/// So the numbers SKILL.md prints are checked against the calibration the build actually +/// ships. Re-running tools/SignsOfAI.Calibration and forgetting the skill fails here. +/// +public class SkillDocumentTests +{ + private static readonly string Skill = File.ReadAllText(Find("SKILL.md")); + + private static string Find(string name) + { + var dir = new DirectoryInfo(AppContext.BaseDirectory); + while (dir is not null && !File.Exists(Path.Combine(dir.FullName, name))) + dir = dir.Parent; + + Assert.NotNull(dir); + return Path.Combine(dir!.FullName, name); + } + + [Fact] + public void It_quotes_the_boundary_this_build_actually_uses() + { + var threshold = PublishedCalibration.Current?.RecommendedThreshold; + Assert.NotNull(threshold); + + Assert.Contains($"{threshold!.Value:0}/100", Skill); + } + + [Fact] + public void It_quotes_the_corpus_and_the_interval_this_build_actually_ships() + { + var published = PublishedCalibration.Current; + Assert.NotNull(published); + + Assert.Contains($"{published!.FlaggedAtThreshold} of {published.Texts}", Skill); + Assert.Contains($"{published.RateHigh * 100:0.0}%", Skill); + } + + /// + /// The rate is only quotable for languages the corpus contains, and the skill has to name them + /// rather than let an assistant assume its own language is covered. + /// + [Fact] + public void It_names_the_languages_that_have_a_measured_rate() + { + var languages = PublishedCalibration.Current?.Languages ?? []; + Assert.NotEmpty(languages); + + foreach (var name in languages.Select(l => l.Language switch + { + "en" => "English", + "es" => "Spanish", + var other => other, + })) + { + Assert.Contains(name, Skill); + } + } + + /// + /// The same decision LocaleFileTests guards for the interface, guarded here for the + /// instructions: a low score is a fact about this tool, never a claim that a person wrote + /// something. An assistant reading a skill that says otherwise would repeat it verbatim. + /// + [Theory] + [InlineData("cannot determine who wrote")] // the frontmatter, which every installer shows + [InlineData("not evidence a human wrote it")] // finding nothing is not a finding + [InlineData("fact about the tool")] // the wording #32 settled for the report + [InlineData("no result meaning")] // the writer baseline has no "someone else" answer + public void It_refuses_to_claim_authorship(string required) => + Assert.Contains(required, Skill, StringComparison.OrdinalIgnoreCase); + + [Fact] + public void It_carries_the_frontmatter_an_installer_reads() + { + Assert.StartsWith("---", Skill); + + var frontmatter = Skill.Split("---", 3)[1]; + Assert.Contains("name: signs-of-ai", frontmatter); + Assert.Contains("description:", frontmatter); + } +}