articulate-writing 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. articulate_writing-0.1.0/.gitattributes +7 -0
  2. articulate_writing-0.1.0/.github/workflows/ci.yml +48 -0
  3. articulate_writing-0.1.0/.github/workflows/publish.yml +41 -0
  4. articulate_writing-0.1.0/.gitignore +34 -0
  5. articulate_writing-0.1.0/.pre-commit-hooks.yaml +7 -0
  6. articulate_writing-0.1.0/LICENSE +110 -0
  7. articulate_writing-0.1.0/PKG-INFO +161 -0
  8. articulate_writing-0.1.0/README.md +138 -0
  9. articulate_writing-0.1.0/action.yml +48 -0
  10. articulate_writing-0.1.0/corpus/ai/blog-intro.md +1 -0
  11. articulate_writing-0.1.0/corpus/ai/email-outreach.md +1 -0
  12. articulate_writing-0.1.0/corpus/ai/flagship-clean.pangram +1 -0
  13. articulate_writing-0.1.0/corpus/ai/flagship-clean.txt +111 -0
  14. articulate_writing-0.1.0/corpus/ai/medium.md +1 -0
  15. articulate_writing-0.1.0/corpus/ai/slop-heavy.md +1 -0
  16. articulate_writing-0.1.0/corpus/human/declaration-preamble.txt +1 -0
  17. articulate_writing-0.1.0/corpus/human/gettysburg.txt +1 -0
  18. articulate_writing-0.1.0/corpus/human/lincoln-2nd-inaugural.txt +1 -0
  19. articulate_writing-0.1.0/corpus/human/ptacek-tweets.pangram +1 -0
  20. articulate_writing-0.1.0/corpus/human/ptacek-tweets.txt +1 -0
  21. articulate_writing-0.1.0/docs/README.md +28 -0
  22. articulate_writing-0.1.0/docs/boundaries.md +71 -0
  23. articulate_writing-0.1.0/docs/cli.md +104 -0
  24. articulate_writing-0.1.0/docs/features.md +172 -0
  25. articulate_writing-0.1.0/docs/getting-started.md +112 -0
  26. articulate_writing-0.1.0/docs/walkthrough.md +165 -0
  27. articulate_writing-0.1.0/editors/jetbrains/README.md +36 -0
  28. articulate_writing-0.1.0/editors/vscode/.vscodeignore +7 -0
  29. articulate_writing-0.1.0/editors/vscode/README.md +32 -0
  30. articulate_writing-0.1.0/editors/vscode/package-lock.json +140 -0
  31. articulate_writing-0.1.0/editors/vscode/package.json +53 -0
  32. articulate_writing-0.1.0/editors/vscode/src/extension.ts +76 -0
  33. articulate_writing-0.1.0/editors/vscode/tsconfig.json +15 -0
  34. articulate_writing-0.1.0/pyproject.toml +38 -0
  35. articulate_writing-0.1.0/src/articulate/__init__.py +13 -0
  36. articulate_writing-0.1.0/src/articulate/bench.py +143 -0
  37. articulate_writing-0.1.0/src/articulate/cli.py +459 -0
  38. articulate_writing-0.1.0/src/articulate/detector.py +1236 -0
  39. articulate_writing-0.1.0/src/articulate/editor.py +563 -0
  40. articulate_writing-0.1.0/src/articulate/genres.py +129 -0
  41. articulate_writing-0.1.0/src/articulate/lsp_server.py +143 -0
  42. articulate_writing-0.1.0/src/articulate/mcp_server.py +195 -0
  43. articulate_writing-0.1.0/src/articulate/modes.py +238 -0
  44. articulate_writing-0.1.0/src/articulate/profiles.py +175 -0
  45. articulate_writing-0.1.0/src/articulate/py.typed +0 -0
  46. articulate_writing-0.1.0/src/articulate/pysource.py +74 -0
  47. articulate_writing-0.1.0/src/articulate/receipt.py +193 -0
  48. articulate_writing-0.1.0/tests/conftest.py +5 -0
  49. articulate_writing-0.1.0/tests/test_audit.py +110 -0
  50. articulate_writing-0.1.0/tests/test_audit_cli.py +137 -0
  51. articulate_writing-0.1.0/tests/test_audit_hardening.py +124 -0
  52. articulate_writing-0.1.0/tests/test_binary_guard.py +71 -0
  53. articulate_writing-0.1.0/tests/test_detector.py +99 -0
  54. articulate_writing-0.1.0/tests/test_dogfood.py +41 -0
  55. articulate_writing-0.1.0/tests/test_editor.py +78 -0
  56. articulate_writing-0.1.0/tests/test_genres.py +148 -0
  57. articulate_writing-0.1.0/tests/test_injection.py +73 -0
  58. articulate_writing-0.1.0/tests/test_lsp.py +74 -0
  59. articulate_writing-0.1.0/tests/test_modes.py +53 -0
  60. articulate_writing-0.1.0/tests/test_receipt.py +66 -0
  61. articulate_writing-0.1.0/tests/test_redos.py +78 -0
  62. articulate_writing-0.1.0/tests/test_science.py +137 -0
  63. articulate_writing-0.1.0/tests/test_spans.py +85 -0
  64. articulate_writing-0.1.0/tests/test_thresholds.py +66 -0
@@ -0,0 +1,7 @@
1
+ # Normalize line endings to LF in the repository, so a CRLF/LF checkout never
2
+ # rewrites a file and never reads as a spurious change (the receipt text hash is
3
+ # byte-exact). Matches the CLI's own line-ending canonicalization.
4
+ * text=auto eol=lf
5
+ *.png binary
6
+ *.jpg binary
7
+ *.gif binary
@@ -0,0 +1,48 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python: ["3.9", "3.10", "3.11", "3.12"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: ${{ matrix.python }}
19
+ - run: pip install -e ".[dev]"
20
+ # The suite gates on the benchmark (recall/specificity), the dogfood check
21
+ # (our own prose passes our own standard), the mode/receipt/LSP contracts,
22
+ # and the ReDoS safety test.
23
+ - run: python -m pytest -q
24
+
25
+ articulate-self-check:
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ - uses: actions/setup-python@v5
30
+ with:
31
+ python-version: "3.12"
32
+ - run: pip install -e .
33
+ # Dogfood in CI: our shipped docs must pass our own gate.
34
+ - run: python -m articulate.cli check README.md docs/*.md --gate
35
+
36
+ build:
37
+ runs-on: ubuntu-latest
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+ - uses: actions/setup-python@v5
41
+ with:
42
+ python-version: "3.12"
43
+ - run: pip install build
44
+ - run: python -m build
45
+ - uses: actions/upload-artifact@v4
46
+ with:
47
+ name: dist
48
+ path: dist/*
@@ -0,0 +1,41 @@
1
+ name: Publish to PyPI
2
+
3
+ # Publishes the package when a GitHub Release is published, or on a manual run.
4
+ # Uses PyPI Trusted Publishing (OIDC): no API token is stored in the repository.
5
+ # One-time setup on PyPI: add a pending publisher for project "articulate-writing"
6
+ # pointing at this repository, the workflow file "publish.yml", and the environment
7
+ # "pypi". See https://docs.pypi.org/trusted-publishers/.
8
+
9
+ on:
10
+ release:
11
+ types: [published]
12
+ workflow_dispatch:
13
+
14
+ jobs:
15
+ build:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+ - run: pip install build twine
23
+ - run: python -m build
24
+ - run: python -m twine check dist/*
25
+ - uses: actions/upload-artifact@v4
26
+ with:
27
+ name: dist
28
+ path: dist/
29
+
30
+ publish:
31
+ needs: build
32
+ runs-on: ubuntu-latest
33
+ environment: pypi
34
+ permissions:
35
+ id-token: write # required for OIDC Trusted Publishing
36
+ steps:
37
+ - uses: actions/download-artifact@v4
38
+ with:
39
+ name: dist
40
+ path: dist/
41
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,34 @@
1
+ # Secrets / env (standard workspace rule; the negation must follow .env.*)
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+ .envrc
6
+ .envrc.local
7
+ CLAUDE.local.md
8
+
9
+ # Python
10
+ __pycache__/
11
+ *.py[cod]
12
+ *.egg-info/
13
+ .eggs/
14
+ build/
15
+ dist/
16
+ .pytest_cache/
17
+ .mypy_cache/
18
+ .ruff_cache/
19
+ .venv/
20
+ venv/
21
+
22
+ # Editor extension deps and build output (package.json/-lock.json stay tracked)
23
+ node_modules/
24
+ out/
25
+ *.vsix
26
+
27
+ # OS
28
+ .DS_Store
29
+ Thumbs.db
30
+
31
+ # Internal-only: strategy/scope with local paths and pricing, and the migration
32
+ # checklist that references local tooling. Kept out of the public repo.
33
+ project-docs/
34
+ MIGRATION.md
@@ -0,0 +1,7 @@
1
+ - id: articulate
2
+ name: Articulate writing check
3
+ description: Flag AI and prose tells; fail the commit on a blocked file.
4
+ entry: python -m articulate.cli check --gate
5
+ language: python
6
+ types_or: [markdown, plain-text]
7
+ require_serial: false
@@ -0,0 +1,110 @@
1
+ # Functional Source License, Version 1.1, MIT Future License
2
+
3
+ ## Abbreviation
4
+
5
+ FSL-1.1-MIT
6
+
7
+ ## Notice
8
+
9
+ Copyright 2026 Zain Dana Harper
10
+
11
+ ## Terms and Conditions
12
+
13
+ ### Licensor ("We")
14
+
15
+ The party offering the Software under these Terms and Conditions.
16
+
17
+ ### The Software
18
+
19
+ The "Software" is each version of the software that we make available under
20
+ these Terms and Conditions, as indicated by our inclusion of these Terms and
21
+ Conditions with the Software.
22
+
23
+ ### License Grant
24
+
25
+ Subject to your compliance with this License Grant and the Patents,
26
+ Redistribution and Trademark clauses below, we hereby grant you the right to
27
+ use, copy, modify, create derivative works, publicly perform, publicly display
28
+ and redistribute the Software for any Permitted Purpose identified below.
29
+
30
+ ### Permitted Purpose
31
+
32
+ A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
33
+ means making the Software available to others in a commercial product or
34
+ service that:
35
+
36
+ 1. substitutes for the Software;
37
+
38
+ 2. substitutes for any other product or service we offer using the Software
39
+ that exists as of the date we make the Software available; or
40
+
41
+ 3. offers the same or substantially similar functionality as the Software.
42
+
43
+ Permitted Purposes specifically include using the Software:
44
+
45
+ 1. for your internal use and access;
46
+
47
+ 2. for non-commercial education;
48
+
49
+ 3. for non-commercial research; and
50
+
51
+ 4. in connection with professional services that you provide to a licensee
52
+ using the Software in accordance with these Terms and Conditions.
53
+
54
+ ### Patents
55
+
56
+ To the extent your use for a Permitted Purpose would necessarily infringe our
57
+ patents, the license grant above includes a license under our patents. If you
58
+ make a claim against any party that the Software infringes or contributes to
59
+ the infringement of any patent, then your patent license to the Software ends
60
+ immediately.
61
+
62
+ ### Redistribution
63
+
64
+ The Terms and Conditions apply to all copies, modifications and derivatives of
65
+ the Software.
66
+
67
+ If you redistribute any copies, modifications or derivatives of the Software,
68
+ you must include a copy of or a link to these Terms and Conditions and not
69
+ remove any copyright notices provided in or with the Software.
70
+
71
+ ### Disclaimer
72
+
73
+ THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
74
+ IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
75
+ PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
76
+
77
+ IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
78
+ SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
79
+ EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
80
+
81
+ ### Trademarks
82
+
83
+ Except for displaying the License Details and identifying us as the origin of
84
+ the Software, you have no right under these Terms and Conditions to use our
85
+ trademarks, trade names, service marks or product names.
86
+
87
+ ## Grant of Future License
88
+
89
+ We hereby irrevocably grant you an additional license to use the Software under
90
+ the MIT license that is effective on the second anniversary of the date we make
91
+ the Software available. On or after that date, you may use the Software under
92
+ the MIT license, in which case the following will apply:
93
+
94
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
95
+ this software and associated documentation files (the "Software"), to deal in
96
+ the Software without restriction, including without limitation the rights to
97
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
98
+ of the Software, and to permit persons to whom the Software is furnished to do
99
+ so, subject to the following conditions:
100
+
101
+ The above copyright notice and this permission notice shall be included in all
102
+ copies or substantial portions of the Software.
103
+
104
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
105
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
106
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
107
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
108
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
109
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
110
+ SOFTWARE.
@@ -0,0 +1,161 @@
1
+ Metadata-Version: 2.5
2
+ Name: articulate-writing
3
+ Version: 0.1.0
4
+ Summary: Local, re-derivable writing-quality and AI-tell detection and editing: register profiles, writing modes, a genre axis, and content-free audit receipts.
5
+ Project-URL: Homepage, https://github.com/HarperZ9/articulate
6
+ Project-URL: Repository, https://github.com/HarperZ9/articulate
7
+ Project-URL: Documentation, https://github.com/HarperZ9/articulate/tree/main/docs
8
+ Project-URL: Issues, https://github.com/HarperZ9/articulate/issues
9
+ Author: Zain Dana Harper
10
+ License-Expression: LicenseRef-FSL-1.1-MIT
11
+ License-File: LICENSE
12
+ Keywords: ai-detection,audit,editor,linter,prose,quality,receipt,writing
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Text Processing :: Linguistic
17
+ Requires-Python: >=3.9
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest>=7; extra == 'dev'
20
+ Provides-Extra: mcp
21
+ Requires-Dist: fastmcp>=3; extra == 'mcp'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # Articulate
25
+
26
+ A local writing-quality and AI-tell detection and editing tool. It flags the
27
+ prose devices and machine-writing tells that make text read as generated, scores
28
+ how machine-textured a passage is, and (with an LLM backend) rewrites prose to a
29
+ plain, skilled standard. The core runs standard-library-only with no network
30
+ call. Detection quality and writing quality are the goals; a detector score is a
31
+ benchmark and a byproduct, never something the tool optimizes toward, and it is
32
+ not an evasion tool.
33
+
34
+ ## Documentation
35
+
36
+ Full docs are in [`docs/`](docs/): [getting started](docs/getting-started.md), a
37
+ thorough [walkthrough](docs/walkthrough.md), the [feature reference](docs/features.md),
38
+ the [CLI reference](docs/cli.md), and the [boundaries](docs/boundaries.md) that
39
+ say what a verdict and a receipt mean and what they never claim.
40
+
41
+ ## What it does
42
+
43
+ - **Detect.** Flags banned rhetorical devices (antithesis including keyword-free
44
+ parallel-negation contrast pairs, corrective negation, rule-of-three, em-dashes,
45
+ filler intensifiers, corporate verbs), current frontier-model register,
46
+ marketing, and email/blog tells, and Williams/Orwell signals (expletive openers,
47
+ nominalization density, passive voice, adverb density, cadence uniformity,
48
+ opener repetition). Emits a graded 0-100 texture score and a clean/flagged gate.
49
+ - **Adapt by register.** A profile system (procedure, commit, research, readme,
50
+ essay, narrative, and more) sets which findings block. Fiction gates nothing;
51
+ procedures and essays gate strictly. Profiles resolve from `--profile`, an
52
+ in-file `writing-profile:` tag, or the file path.
53
+ - **Choose a mode or a genre.** A writing mode crosses a domain register with an
54
+ articulation need, such as `memo/argue` or `technical-docs/explain`. The genre
55
+ axis reads narrative and expressive prose by its own convention: `literary-fiction`,
56
+ `genre-fiction`, `ya-fiction`, `memoir`, `screenplay`, `poetry`. Under a fiction
57
+ genre, quoted speech is masked out of the device passes so a character's line is
58
+ never scored as the author's prose; the craft devices report but never block; and
59
+ a report-only lexicon flags generation artifacts such as the somatic cliche or the
60
+ "could not help but" reflexive. Screenplay classifies Fountain roles first, so only
61
+ action lines face the device gate. Poetry reads by the line and drops the
62
+ craft-device categories from its report. Run `articulate modes` to list them.
63
+ - **Edit.** `judge` reads the judgment-level failures a regex cannot see
64
+ (confident emptiness, vague abstraction, hedging with no position, weak verbs).
65
+ `fix` rewrites to the standard, self-checked against the detector. `polish`
66
+ loops until five qualities (concreteness, commitment, economy, rhythm, a
67
+ restatable fact per paragraph) clear a bar. Gated on writing quality, never a
68
+ detector score. Needs an LLM backend (local model or the `claude` CLI).
69
+
70
+ ## Use
71
+
72
+ ```bash
73
+ # lint (exit 1 when blocked under the file's profile)
74
+ python -m articulate.cli check path/to/doc.md --gate
75
+ python -m articulate.cli check essay.md --profile essay --verbose
76
+ echo "some prose" | python -m articulate.cli score
77
+
78
+ # library
79
+ python -c "import articulate; print(articulate.check_text('...', profile=articulate.profiles.load('research'))['gate'])"
80
+
81
+ # receipt: a re-derivable verdict (Match / Drift / Unverifiable)
82
+ python -m articulate.cli receipt doc.md --profile research > doc.receipt.json
83
+ python -m articulate.cli verify doc.receipt.json doc.md # replay; exit 0/1/2
84
+
85
+ # content-free audit receipt: replayable, but stores no verbatim text (drop or hash
86
+ # the matched substring). For a team that must retain a record without the source.
87
+ python -m articulate.cli receipt doc.md --redact drop --reviewer alice > receipts/doc.json
88
+ python -m articulate.cli check doc.md --content-free --sarif > doc.sarif # no substrings
89
+
90
+ # audit: query committed receipts locally (no server), and re-verify they still hold
91
+ python -m articulate.cli audit receipts/ # recorded verdicts, blocked rules
92
+ python -m articulate.cli audit receipts/ --reverify --gate # exit 1 if a source drifted
93
+
94
+ # SARIF for CI (GitHub Code Scanning, Azure, reviewdog)
95
+ python -m articulate.cli check src/**/*.md --sarif > articulate.sarif
96
+
97
+ # LSP server (inline squiggles in VS Code, JetBrains via LSP4IJ, Neovim). Stdlib
98
+ # only, no dependency. Point your editor's LSP client at:
99
+ python -m articulate.lsp_server
100
+
101
+ # benchmark (regression-gated corpus) and MCP server
102
+ python -m articulate.bench
103
+ python -m articulate.mcp_server
104
+ ```
105
+
106
+ ### Editor setup
107
+
108
+ The LSP server speaks standard LSP over stdio, so any LSP client can drive it.
109
+ A minimal Neovim registration:
110
+
111
+ ```lua
112
+ vim.lsp.start({ name = "articulate", cmd = { "python", "-m", "articulate.lsp_server" },
113
+ filetypes = { "markdown", "text", "tex" } })
114
+ ```
115
+
116
+ For VS Code, a thin client that launches the same command as a `LanguageClient`
117
+ is all that is needed; no server code lives in the extension.
118
+
119
+ ### Scientific and mathematical writing
120
+
121
+ `academic/prove` and `science-writing/explain` target hard technical exposition:
122
+ stating the idea before the formalism, keeping a roadmap, defining each symbol
123
+ once. The proof mode does not rewrite by default, because a wrong change to a
124
+ quantifier order or an inequality direction changes a theorem; it routes to
125
+ `--judge`, and `--fix` is opt-in. On a `.tex` file the editor masks every math
126
+ span before a rewrite and splices it back byte for byte, so a formula is never
127
+ altered.
128
+
129
+ The boundary is fixed and load-bearing: a clean gate, a low texture score, or a
130
+ Match receipt means the prose was screened under a named ruleset. It says nothing
131
+ about whether the theorem is true. A clearly written proof can still be false, and
132
+ Articulate never checks the mathematics. Correctness comes from referees and proof
133
+ assistants (Lean, Coq, Isabelle), never from this tool.
134
+
135
+ ## Privacy
136
+
137
+ The detector never touches the network. The editor layer defaults to a local
138
+ model where configured, and an `--offline` mode is on the roadmap for air-gapped
139
+ use. A content-free audit receipt keeps no verbatim text: it drops the matched
140
+ substring and the exact offsets, keeping only which rule fired, its tier and
141
+ category, and the line. A team can retain and replay a record without storing the
142
+ sensitive source. Content-free is not zero-leakage: which rules fired and the line
143
+ remain, which for a closed-vocabulary rule narrows the flagged word to that rule's
144
+ small public candidate set. The `hash` mode keeps a sha256 for an equality check
145
+ against a known string, so it is dictionary-reversible for those closed-vocabulary
146
+ rules; use `drop` when the flagged word must stay secret. An auto-filled `reviewer`
147
+ (from `$GITHUB_ACTOR`) records CI attribution, and a named human sign-off needs an
148
+ explicit `--reviewer`. Committed receipts live as long as the repo, with no
149
+ automatic expiry (bounded retention is a later self-hosted tier). The full
150
+ [documentation](docs/) covers each surface and the boundaries in depth.
151
+
152
+ ## Status
153
+
154
+ Pre-1.0. The core detector, profile system, writing modes (including the science
155
+ modes for proofs and technical exposition), the genre axis (fiction, memoir,
156
+ screenplay, poetry), the editor injection boundary, per-span mixed-authorship
157
+ verdicts, a sub-threshold "unverifiable" calibration, binary fail-closed input
158
+ guards, the benchmark, the editor layer, the CLI, the LSP and SARIF surfaces,
159
+ receipts, the content-free audit receipt, and the MCP server are built into this
160
+ one package. A local-model editor backend, a labeled non-native corpus for a
161
+ fairness check, and PyPI packaging are on the roadmap.
@@ -0,0 +1,138 @@
1
+ # Articulate
2
+
3
+ A local writing-quality and AI-tell detection and editing tool. It flags the
4
+ prose devices and machine-writing tells that make text read as generated, scores
5
+ how machine-textured a passage is, and (with an LLM backend) rewrites prose to a
6
+ plain, skilled standard. The core runs standard-library-only with no network
7
+ call. Detection quality and writing quality are the goals; a detector score is a
8
+ benchmark and a byproduct, never something the tool optimizes toward, and it is
9
+ not an evasion tool.
10
+
11
+ ## Documentation
12
+
13
+ Full docs are in [`docs/`](docs/): [getting started](docs/getting-started.md), a
14
+ thorough [walkthrough](docs/walkthrough.md), the [feature reference](docs/features.md),
15
+ the [CLI reference](docs/cli.md), and the [boundaries](docs/boundaries.md) that
16
+ say what a verdict and a receipt mean and what they never claim.
17
+
18
+ ## What it does
19
+
20
+ - **Detect.** Flags banned rhetorical devices (antithesis including keyword-free
21
+ parallel-negation contrast pairs, corrective negation, rule-of-three, em-dashes,
22
+ filler intensifiers, corporate verbs), current frontier-model register,
23
+ marketing, and email/blog tells, and Williams/Orwell signals (expletive openers,
24
+ nominalization density, passive voice, adverb density, cadence uniformity,
25
+ opener repetition). Emits a graded 0-100 texture score and a clean/flagged gate.
26
+ - **Adapt by register.** A profile system (procedure, commit, research, readme,
27
+ essay, narrative, and more) sets which findings block. Fiction gates nothing;
28
+ procedures and essays gate strictly. Profiles resolve from `--profile`, an
29
+ in-file `writing-profile:` tag, or the file path.
30
+ - **Choose a mode or a genre.** A writing mode crosses a domain register with an
31
+ articulation need, such as `memo/argue` or `technical-docs/explain`. The genre
32
+ axis reads narrative and expressive prose by its own convention: `literary-fiction`,
33
+ `genre-fiction`, `ya-fiction`, `memoir`, `screenplay`, `poetry`. Under a fiction
34
+ genre, quoted speech is masked out of the device passes so a character's line is
35
+ never scored as the author's prose; the craft devices report but never block; and
36
+ a report-only lexicon flags generation artifacts such as the somatic cliche or the
37
+ "could not help but" reflexive. Screenplay classifies Fountain roles first, so only
38
+ action lines face the device gate. Poetry reads by the line and drops the
39
+ craft-device categories from its report. Run `articulate modes` to list them.
40
+ - **Edit.** `judge` reads the judgment-level failures a regex cannot see
41
+ (confident emptiness, vague abstraction, hedging with no position, weak verbs).
42
+ `fix` rewrites to the standard, self-checked against the detector. `polish`
43
+ loops until five qualities (concreteness, commitment, economy, rhythm, a
44
+ restatable fact per paragraph) clear a bar. Gated on writing quality, never a
45
+ detector score. Needs an LLM backend (local model or the `claude` CLI).
46
+
47
+ ## Use
48
+
49
+ ```bash
50
+ # lint (exit 1 when blocked under the file's profile)
51
+ python -m articulate.cli check path/to/doc.md --gate
52
+ python -m articulate.cli check essay.md --profile essay --verbose
53
+ echo "some prose" | python -m articulate.cli score
54
+
55
+ # library
56
+ python -c "import articulate; print(articulate.check_text('...', profile=articulate.profiles.load('research'))['gate'])"
57
+
58
+ # receipt: a re-derivable verdict (Match / Drift / Unverifiable)
59
+ python -m articulate.cli receipt doc.md --profile research > doc.receipt.json
60
+ python -m articulate.cli verify doc.receipt.json doc.md # replay; exit 0/1/2
61
+
62
+ # content-free audit receipt: replayable, but stores no verbatim text (drop or hash
63
+ # the matched substring). For a team that must retain a record without the source.
64
+ python -m articulate.cli receipt doc.md --redact drop --reviewer alice > receipts/doc.json
65
+ python -m articulate.cli check doc.md --content-free --sarif > doc.sarif # no substrings
66
+
67
+ # audit: query committed receipts locally (no server), and re-verify they still hold
68
+ python -m articulate.cli audit receipts/ # recorded verdicts, blocked rules
69
+ python -m articulate.cli audit receipts/ --reverify --gate # exit 1 if a source drifted
70
+
71
+ # SARIF for CI (GitHub Code Scanning, Azure, reviewdog)
72
+ python -m articulate.cli check src/**/*.md --sarif > articulate.sarif
73
+
74
+ # LSP server (inline squiggles in VS Code, JetBrains via LSP4IJ, Neovim). Stdlib
75
+ # only, no dependency. Point your editor's LSP client at:
76
+ python -m articulate.lsp_server
77
+
78
+ # benchmark (regression-gated corpus) and MCP server
79
+ python -m articulate.bench
80
+ python -m articulate.mcp_server
81
+ ```
82
+
83
+ ### Editor setup
84
+
85
+ The LSP server speaks standard LSP over stdio, so any LSP client can drive it.
86
+ A minimal Neovim registration:
87
+
88
+ ```lua
89
+ vim.lsp.start({ name = "articulate", cmd = { "python", "-m", "articulate.lsp_server" },
90
+ filetypes = { "markdown", "text", "tex" } })
91
+ ```
92
+
93
+ For VS Code, a thin client that launches the same command as a `LanguageClient`
94
+ is all that is needed; no server code lives in the extension.
95
+
96
+ ### Scientific and mathematical writing
97
+
98
+ `academic/prove` and `science-writing/explain` target hard technical exposition:
99
+ stating the idea before the formalism, keeping a roadmap, defining each symbol
100
+ once. The proof mode does not rewrite by default, because a wrong change to a
101
+ quantifier order or an inequality direction changes a theorem; it routes to
102
+ `--judge`, and `--fix` is opt-in. On a `.tex` file the editor masks every math
103
+ span before a rewrite and splices it back byte for byte, so a formula is never
104
+ altered.
105
+
106
+ The boundary is fixed and load-bearing: a clean gate, a low texture score, or a
107
+ Match receipt means the prose was screened under a named ruleset. It says nothing
108
+ about whether the theorem is true. A clearly written proof can still be false, and
109
+ Articulate never checks the mathematics. Correctness comes from referees and proof
110
+ assistants (Lean, Coq, Isabelle), never from this tool.
111
+
112
+ ## Privacy
113
+
114
+ The detector never touches the network. The editor layer defaults to a local
115
+ model where configured, and an `--offline` mode is on the roadmap for air-gapped
116
+ use. A content-free audit receipt keeps no verbatim text: it drops the matched
117
+ substring and the exact offsets, keeping only which rule fired, its tier and
118
+ category, and the line. A team can retain and replay a record without storing the
119
+ sensitive source. Content-free is not zero-leakage: which rules fired and the line
120
+ remain, which for a closed-vocabulary rule narrows the flagged word to that rule's
121
+ small public candidate set. The `hash` mode keeps a sha256 for an equality check
122
+ against a known string, so it is dictionary-reversible for those closed-vocabulary
123
+ rules; use `drop` when the flagged word must stay secret. An auto-filled `reviewer`
124
+ (from `$GITHUB_ACTOR`) records CI attribution, and a named human sign-off needs an
125
+ explicit `--reviewer`. Committed receipts live as long as the repo, with no
126
+ automatic expiry (bounded retention is a later self-hosted tier). The full
127
+ [documentation](docs/) covers each surface and the boundaries in depth.
128
+
129
+ ## Status
130
+
131
+ Pre-1.0. The core detector, profile system, writing modes (including the science
132
+ modes for proofs and technical exposition), the genre axis (fiction, memoir,
133
+ screenplay, poetry), the editor injection boundary, per-span mixed-authorship
134
+ verdicts, a sub-threshold "unverifiable" calibration, binary fail-closed input
135
+ guards, the benchmark, the editor layer, the CLI, the LSP and SARIF surfaces,
136
+ receipts, the content-free audit receipt, and the MCP server are built into this
137
+ one package. A local-model editor backend, a labeled non-native corpus for a
138
+ fairness check, and PyPI packaging are on the roadmap.
@@ -0,0 +1,48 @@
1
+ name: "Articulate"
2
+ description: "Writing-quality and AI-tell checks with SARIF output for inline PR annotations."
3
+ branding:
4
+ icon: "edit-3"
5
+ color: "green"
6
+ inputs:
7
+ files:
8
+ description: "Files or globs to check (space-separated)."
9
+ required: false
10
+ default: "**/*.md"
11
+ profile:
12
+ description: "Force a register profile (optional)."
13
+ required: false
14
+ default: ""
15
+ python-version:
16
+ description: "Python version to run under."
17
+ required: false
18
+ default: "3.12"
19
+ audit-path:
20
+ description: "Directory of committed receipts to re-verify; fails the build on any drift."
21
+ required: false
22
+ default: ""
23
+ runs:
24
+ using: "composite"
25
+ steps:
26
+ - uses: actions/setup-python@v5
27
+ with:
28
+ python-version: ${{ inputs.python-version }}
29
+ # Installs the checked-out package. Swap for `pip install articulate-writing`
30
+ # once the package is published to PyPI.
31
+ - run: pip install "${{ github.action_path }}"
32
+ shell: bash
33
+ - run: |
34
+ PROFILE=""
35
+ if [ -n "${{ inputs.profile }}" ]; then PROFILE="--profile ${{ inputs.profile }}"; fi
36
+ python -m articulate.cli check ${{ inputs.files }} $PROFILE --sarif > articulate.sarif
37
+ shell: bash
38
+ - uses: github/codeql-action/upload-sarif@v3
39
+ with:
40
+ sarif_file: articulate.sarif
41
+ category: articulate
42
+ # Optional: re-verify committed receipts still hold against their source, and
43
+ # fail the build on any drift. Wire a receipts directory via the audit-path input.
44
+ - run: |
45
+ if [ -n "${{ inputs.audit-path }}" ]; then
46
+ python -m articulate.cli audit "${{ inputs.audit-path }}" --reverify --gate
47
+ fi
48
+ shell: bash
@@ -0,0 +1 @@
1
+ In today's fast-paced digital world, staying ahead of the curve is more important than ever. As technology continues to evolve, businesses must adapt to remain competitive. Whether you're a seasoned professional or just starting out, this comprehensive guide will help you navigate the ever-changing landscape. Studies have shown that companies who embrace innovation consistently outperform their peers. Moreover, by fostering a culture of collaboration, organizations can unlock their full potential. Ultimately, the possibilities are endless, and the future is bright.
@@ -0,0 +1 @@
1
+ I hope this email finds you well. I wanted to reach out to touch base regarding our upcoming collaboration. I'm thrilled to announce that we've made significant progress on the initiative. By leveraging our combined strengths, I'm confident we can unlock tremendous value and drive meaningful outcomes. Please don't hesitate to reach out if you have any questions. Circling back on the earlier thread, it's essential to ensure that we stay aligned. Looking forward to connecting further and delivering impactful results together.