@domandigital/craft 0.6.0 → 0.8.0
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.
- package/CHANGELOG.md +36 -0
- package/CHARACTER.md +280 -20
- package/COPY.md +280 -0
- package/README.md +12 -0
- package/dist/audit-W57YDZ2K.js +20 -0
- package/dist/audit.cjs +901 -103
- package/dist/audit.d.cts +16 -1
- package/dist/audit.d.ts +16 -1
- package/dist/audit.js +899 -102
- package/dist/chunk-35OX6C4H.js +112 -0
- package/dist/{chunk-GWMSSRIQ.js → chunk-CZHRNPDD.js} +1521 -934
- package/dist/chunk-K253ZXB3.js +458 -0
- package/dist/{chunk-HYGFP3LB.js → chunk-OGSYVCMU.js} +40 -3
- package/dist/chunk-OVOH7KZY.js +163 -0
- package/dist/{cli-7ME4VIRG.js → chunk-Q4MN3OXF.js} +50 -101
- package/dist/chunk-ULYKASLA.js +175 -0
- package/dist/chunk-X65RDW65.js +364 -0
- package/dist/cli-F7ONSKZS.js +21 -0
- package/dist/cli-FKJOMOOF.js +15 -0
- package/dist/{cli-F7W4F3ER.js → cli-KA3JDNDH.js} +14 -168
- package/dist/cli-LZ7R6KJM.js +312 -0
- package/dist/cli-SHLNRHK6.js +16 -0
- package/dist/cli.js +2 -1
- package/dist/index.cjs +1528 -99
- package/dist/index.d.cts +365 -4
- package/dist/index.d.ts +365 -4
- package/dist/index.js +1493 -96
- package/package.json +4 -2
- package/word-lists.json +164 -0
package/COPY.md
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# The house copy standard
|
|
2
|
+
|
|
3
|
+
**Canonical.** Every client repo, every session, every piece of outbound
|
|
4
|
+
writing. This file ships in `@domandigital/craft`, next to the tells that
|
|
5
|
+
enforce it. Anything else claiming to be the copy rules is a copy: link here.
|
|
6
|
+
|
|
7
|
+
- The rules are this file.
|
|
8
|
+
- The detectors are craft's copy tells: `craft tells list` shows every one,
|
|
9
|
+
with why it is a default and what to do instead.
|
|
10
|
+
- The house policy, which tells fail a commit, is `src/character/house.ts`.
|
|
11
|
+
`craft copy --gate` applies it. `copy-check` in claude-kit is a thin wrapper
|
|
12
|
+
over that, and the pre-commit hook runs it.
|
|
13
|
+
|
|
14
|
+
## Why
|
|
15
|
+
|
|
16
|
+
A reader who has seen a thousand generated pages recognises one in a line.
|
|
17
|
+
None of the patterns below is AI-only, and people use all of them. The signal
|
|
18
|
+
is **stacking**: one is plausible, three on a page reads as machine output.
|
|
19
|
+
|
|
20
|
+
What makes copy read as written by a person is not the absence of tells. It is
|
|
21
|
+
the presence of facts: a place, a time, a number, a name, who does the work.
|
|
22
|
+
The rules below remove the defaults; the writing directives say what to put in
|
|
23
|
+
their place.
|
|
24
|
+
|
|
25
|
+
## Blocking tier
|
|
26
|
+
|
|
27
|
+
A single hit fails the check. These are not judgment calls.
|
|
28
|
+
|
|
29
|
+
- **Em dashes.** Use a full stop, a comma, a colon or a middot. En dashes for
|
|
30
|
+
genuine ranges (`Monday–Saturday`, `9am–5pm`) are correct and stay.
|
|
31
|
+
- **Emoji** in shipped copy. Typographic marks are fine: `★` for a rating, `·`
|
|
32
|
+
as a separator, `→` in a link.
|
|
33
|
+
- **Contrastive negation as a template**, in every form:
|
|
34
|
+
- copular: "It's not a website, it's a growth engine"
|
|
35
|
+
- verbal: "I don't teach the treatment you'd choose: I teach the standard"
|
|
36
|
+
- "not just X" / "not merely" / "not simply" / "more than just"
|
|
37
|
+
- "X isn't a job, it's art"
|
|
38
|
+
- "less about X, more about Y"
|
|
39
|
+
- parallel participle: "shipped from here, not imported from Seoul". Both
|
|
40
|
+
sides must be real participles, so "cosmetics, not medicines" (a plain
|
|
41
|
+
factual contrast) passes.
|
|
42
|
+
- **A "No X" claim as the whole string.** "NO JARGON GUIDE", "No catch." A
|
|
43
|
+
short standalone "No X" reads as a slapped-on badge. Empty-state and error
|
|
44
|
+
text is exempt ("No results found", "No bookings yet"), as is anything under
|
|
45
|
+
a signed-in route (`/admin/`, `/portal/`, `/dashboard/`).
|
|
46
|
+
- **"No X, no Y" as a list**, anywhere in a sentence. "No obligation, no
|
|
47
|
+
spam." Defining the business by what it is not, itemised.
|
|
48
|
+
- The phrase list: `let's dive in`, `let's unpack`, `let's break this down`,
|
|
49
|
+
`here's the thing`, `it's important to note`, `at its core`,
|
|
50
|
+
`the key takeaway`, `the reality is`, `the truth is`,
|
|
51
|
+
`in today's rapidly evolving…`, `increasingly digital`,
|
|
52
|
+
`whether you're a X or a Y`, `seamless(ly)`, `unlock your…`,
|
|
53
|
+
`meaningful impact`, `foster collaboration`, `empower`, `leverage`,
|
|
54
|
+
`harness`, `delve`, `a comprehensive guide to`, `plain English`.
|
|
55
|
+
|
|
56
|
+
Also blocking: `in the event that`, `at no additional cost to you`, `rest
|
|
57
|
+
assured`, `it is worth noting that`, `when it comes to`, `we've got you
|
|
58
|
+
covered`, `dive into`, `unlock the power`, `look no further`.
|
|
59
|
+
|
|
60
|
+
**Buzzwords**: synergy, cutting-edge, best-in-class, best of breed,
|
|
61
|
+
world-class, state of the art, game-changer, paradigm shift, mission-critical,
|
|
62
|
+
low-hanging fruit, move the needle, circle back, touch base, reach out (use
|
|
63
|
+
"get in touch"), value-add, robust, turnkey, holistic, disruptive.
|
|
64
|
+
|
|
65
|
+
**Negative reassurance**: `no risk`, `never locked in`, `nothing goes wrong`,
|
|
66
|
+
`no hidden fees`, `never pay`, `without the hassle`, `no surprises`. Do not
|
|
67
|
+
name a fear and deny it. Say what they get: "The price on the quote is the
|
|
68
|
+
price you pay", not "no hidden fees".
|
|
69
|
+
|
|
70
|
+
`empower`, `leverage` and `harness` block because a plainer word almost always
|
|
71
|
+
exists. Name the mechanism: "Your team can publish without us", not "we
|
|
72
|
+
empower your team".
|
|
73
|
+
|
|
74
|
+
`plain English` has one house replacement: **"properly explained"**.
|
|
75
|
+
"Explained plainly" swaps a synonym and keeps the tell.
|
|
76
|
+
|
|
77
|
+
## Review tier
|
|
78
|
+
|
|
79
|
+
Printed, never blocks. One is normal English; several stacked is not. Only a
|
|
80
|
+
person reading decides which instance earns its place.
|
|
81
|
+
|
|
82
|
+
| Tell | What it catches |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| `review-phrase` | truly, genuinely, deeply, ultimately, great question, absolutely, one-size-fits-all, and superlative over-claiming: "the single most", "by far the most", "hands down the", "arguably the best", "bar none" |
|
|
85
|
+
| `ai-vocabulary` | tapestry, testament, realm, elevate, nestled, meticulous, unparalleled, moreover; and underscore, showcasing, pivotal, measured 10 to 14 times over-used in post-2022 text |
|
|
86
|
+
| `stock-phrase` | to the next level, one-stop shop, we pride ourselves, passionate about, peace of mind, "so here's", a journey used as a metaphor |
|
|
87
|
+
| `hollow-imperative` | Discover, Experience, Transform, Elevate as the opening verb of a heading or button |
|
|
88
|
+
| `rhetorical-opener` | "Looking for...?", "Tired of...?", "Ready to...?" |
|
|
89
|
+
| `staccato-triplet` | "Fast. Friendly. Local." |
|
|
90
|
+
| `where-x-meets-y` | "Where luxury meets comfort" |
|
|
91
|
+
| `ing-tail` | "..., ensuring peace of mind", "..., highlighting our commitment": significance claimed, no mechanism |
|
|
92
|
+
| `vague-attribution` | "studies show", "experts agree", "it is widely known": a claim credited to nobody |
|
|
93
|
+
| `closing-summary` | "Overall,", "In conclusion,", "In short," opening a paragraph that restates what came before |
|
|
94
|
+
| `false-range` | "from first-time buyers to seasoned investors alike", "everyone from students to retirees" |
|
|
95
|
+
| `vague-word` | innovative, scalable, end-to-end, streamline, solutions. Reported only when a file is named outright |
|
|
96
|
+
| `question-reveal` | "The result? Twice the bookings.", "What does this mean for you? It means...": a staged reveal |
|
|
97
|
+
| `inline-label-list` | three or more bullets in a row opening "**Label:**", where the label repeats the line |
|
|
98
|
+
| `placeholder` | "[Insert client name]", lorem ipsum, `TODO_PLACEHOLDER`. Fine in a draft commit; **zero in anything delivered** |
|
|
99
|
+
| `chatbot-residue` | "Certainly! Here's a revised version", "as an AI language model", "let me know if you'd like me to", chat citation tokens, links tracked `utm_source=chatgpt.com`. **Zero in anything delivered**, and the first candidate for the blocking tier |
|
|
100
|
+
|
|
101
|
+
### Style is not evidence
|
|
102
|
+
|
|
103
|
+
Most tells here are house style: they make copy generic, whoever wrote it. The
|
|
104
|
+
research is clear that an em dash, a rule of three or a rhetorical question
|
|
105
|
+
proves nothing about authorship, and em dash rates even run in opposite
|
|
106
|
+
directions between models. Only `chatbot-residue` is evidence that a chat reply
|
|
107
|
+
was pasted. Report findings as an editorial diagnosis ("three benefit claims
|
|
108
|
+
lack a mechanism; two stock phrases recur"), never as a score or an
|
|
109
|
+
accusation.
|
|
110
|
+
|
|
111
|
+
Two more review patterns no regex can separate from honest English, so they
|
|
112
|
+
are for the read-through only:
|
|
113
|
+
|
|
114
|
+
- **Trailing contrastive negation**: "Real Square data, not a vanity metric."
|
|
115
|
+
The same shape is how a genuine disclaimer is written ("general information,
|
|
116
|
+
not a recommendation").
|
|
117
|
+
- **Abstract noun triads** with no concrete referent: "clarity, consistency,
|
|
118
|
+
confidence".
|
|
119
|
+
|
|
120
|
+
## Density tier
|
|
121
|
+
|
|
122
|
+
Review only, never blocks. Every rule above fires on one line read in
|
|
123
|
+
isolation. These fire on a **rate**: each instance is legitimate English, and
|
|
124
|
+
only the count gives it away. They read documents (Markdown, MDX, text, HTML)
|
|
125
|
+
of 400 words or more, and the allowed count is a floor plus a rate per 1,000
|
|
126
|
+
words, so a proposal is not held to a landing page's limit.
|
|
127
|
+
|
|
128
|
+
Why they exist: a 30-page client proposal passed every per-line rule clean.
|
|
129
|
+
A person then found "rather than" 54 times in 10,500 words, no contractions,
|
|
130
|
+
one sentence pasted onto two pages, a heading repeated as its own first line,
|
|
131
|
+
and an italic one-liner closing almost every page.
|
|
132
|
+
|
|
133
|
+
| Tell | Fires above |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| `phrase-density` | "rather than" (4, +0.9 per 1,000 words), "instead of" (4, +0.7), "actually" (2, +0.35), "properly" (3, +0.4), "genuinely" (1, +0.2), "the single ...est" (1, +0.15), "That is / This is" openers (4, +0.7), "Every" openers (4, +0.6), "Worth noting" openers (3, +0.5), negation-led openers "None of / Not one / Nothing" (5, +1), participle triads "researched, written and referenced" (3, +0.6) |
|
|
136
|
+
| `aphorism-density` | two clipped sentences alone on a line, "It already exists. The job is making it findable." (5, +1.2) |
|
|
137
|
+
| `contraction-scarcity` | under one contraction per 1,000 words, in 800+ words. Formal legal pages are the deliberate exception |
|
|
138
|
+
| `sentence-rhythm` | sentence lengths that barely vary: spread (standard deviation over mean) under 0.42 across 40+ sentences |
|
|
139
|
+
| `repeated-sentence` | a sentence of nine words or more appearing twice, or two sentences sharing a nine-word run |
|
|
140
|
+
| `heading-shape` | headings sharing one shape: "X, and Y", "X, because Y", a bare comma list (3, +0.5) |
|
|
141
|
+
| `heading-echo` | a heading of four words or more repeated as the first words under it |
|
|
142
|
+
|
|
143
|
+
The fix is never to delete every instance. Keep the ones that land hardest and
|
|
144
|
+
vary the rest. That is why a density finding prints a count and an example,
|
|
145
|
+
not a line to go and edit.
|
|
146
|
+
|
|
147
|
+
## Writing directives
|
|
148
|
+
|
|
149
|
+
These are what to do, not what to avoid. Read them **before** drafting. Do not
|
|
150
|
+
read the tell lists before drafting: naming a phrase to avoid puts it in the
|
|
151
|
+
writer's head, and it comes out anyway. Check against the lists afterwards.
|
|
152
|
+
|
|
153
|
+
- **Lead with the fact.** A place, a time, a number, a name. "Gas Safe
|
|
154
|
+
registered since 2009. Same-day callouts in Brackley." beats "Trusted local
|
|
155
|
+
experts".
|
|
156
|
+
- **Concrete nouns beat abstractions.** "Rated 5.0 from 58 reviews" beats
|
|
157
|
+
"trusted by the community".
|
|
158
|
+
- **Name the mechanism.** Not "hassle-free booking", but "Book in three taps.
|
|
159
|
+
We confirm by text within the hour."
|
|
160
|
+
- **Persuade with something checkable.** Sales copy has to sell, and
|
|
161
|
+
enthusiasm is allowed. Persuasion backed by a fact passes; persuasion with no
|
|
162
|
+
fact behind it is the tell.
|
|
163
|
+
- **Concise, scannable, objective.** In usability testing, cutting word count,
|
|
164
|
+
making text scannable and dropping promotional tone each helped, and all
|
|
165
|
+
three together roughly doubled usability (Nielsen Norman Group).
|
|
166
|
+
- **Vary sentence length.** Some long, some short, some flat. Uniform rhythm
|
|
167
|
+
reads generated, and so does uniform wit.
|
|
168
|
+
- **Use contractions** where you would in speech. Formal legal pages are the
|
|
169
|
+
exception.
|
|
170
|
+
- **If the sentence would read the same with a competitor's name in it**, cut
|
|
171
|
+
it or make it specific.
|
|
172
|
+
- **Never reuse a sentence across two clients.** Templated language across
|
|
173
|
+
clients is a stronger tell than any phrase.
|
|
174
|
+
- **Never open with a thesis-statement negation.** "This is a working build,
|
|
175
|
+
not a mockup." Judge by function: if the sentence exists only for the
|
|
176
|
+
contrast, cut it.
|
|
177
|
+
- **Compliance hedging must not eat the sell.** Put trial design and caveats
|
|
178
|
+
where substantiation belongs (an evidence section, an ingredients panel, a
|
|
179
|
+
footnote). In the selling paragraph, name the claim and stop.
|
|
180
|
+
- **End each major section with one action sentence**: what to do next.
|
|
181
|
+
"Book a call to see if we're a fit."
|
|
182
|
+
|
|
183
|
+
## Register: British and professional
|
|
184
|
+
|
|
185
|
+
The tool cannot grade register; a person does. British spelling and phrasing
|
|
186
|
+
(colour, organise, licence as a noun, centre, towards, enquiries). Professional
|
|
187
|
+
and confident, never matey: no over-familiar asides ("with me nowhere near
|
|
188
|
+
it"), no American idiom ("shallow wins", "looks busy"), no "honestly" as an
|
|
189
|
+
intensifier. Do not change a brand name, domain or legal entity wording.
|
|
190
|
+
|
|
191
|
+
## Word limits
|
|
192
|
+
|
|
193
|
+
| Element | Limit |
|
|
194
|
+
| --- | --- |
|
|
195
|
+
| H1 | 10 words |
|
|
196
|
+
| Subhead | 24 words |
|
|
197
|
+
| Paragraph | 2 sentences |
|
|
198
|
+
| Bullet | 7 words |
|
|
199
|
+
|
|
200
|
+
Hyphenated compounds count as one word. Not automated.
|
|
201
|
+
|
|
202
|
+
## Proof rule
|
|
203
|
+
|
|
204
|
+
- **No invented results, testimonials, logos, certifications or numbers.
|
|
205
|
+
Ever.**
|
|
206
|
+
- Every proof point comes from the project's own proof source (a proofs or
|
|
207
|
+
reviews file, the CMS, the client). If one is missing, write
|
|
208
|
+
`TODO_PLACEHOLDER: <the exact field needed>` and say so. Never fill it.
|
|
209
|
+
- A quotation under a named person must be theirs. Copy drafted for someone,
|
|
210
|
+
in quote marks with their job title beneath it, is a fabricated attribution.
|
|
211
|
+
|
|
212
|
+
## CTA rule
|
|
213
|
+
|
|
214
|
+
- One primary CTA label site-wide on commercial pages, in the form **action +
|
|
215
|
+
outcome**: "Book a free strategy call".
|
|
216
|
+
- The secondary CTA is the same everywhere it appears.
|
|
217
|
+
- Labels and links come from the project's config, never retyped per page.
|
|
218
|
+
|
|
219
|
+
## Never edit to pass: quoted words
|
|
220
|
+
|
|
221
|
+
**A real person's own words are exempt from every rule here.** Reviews,
|
|
222
|
+
testimonials, a practitioner's bio, a quote from the client. Editing them to
|
|
223
|
+
pass an audit falsifies a quotation, which is far worse than an em dash. Mark
|
|
224
|
+
the line `copy-ok` or add the file to `.copyauditignore`, and leave it as they
|
|
225
|
+
said it.
|
|
226
|
+
|
|
227
|
+
**Owner-editable copy is advised on, never overridden.** Where a client types
|
|
228
|
+
their own words into a CMS, report what the check finds and tell them. Copy
|
|
229
|
+
that ships from the repo is ours to fix; copy the owner typed is theirs.
|
|
230
|
+
|
|
231
|
+
## Rendered social assets: no raw links
|
|
232
|
+
|
|
233
|
+
A carousel slide, Story, Reel or caption never shows a raw URL: no `https://`,
|
|
234
|
+
no `www.`, no path. Show a bare domain (`RMP-ELECTRICAL.CO.UK`) or a plain
|
|
235
|
+
call to action ("Link in bio"). Keep the full address in source data and let
|
|
236
|
+
the render step transform it. craft cannot enforce this, because raw URLs are
|
|
237
|
+
legitimate almost everywhere else in a repo. The social generator's own render
|
|
238
|
+
gate enforces it, before its first render ships.
|
|
239
|
+
|
|
240
|
+
## Suppressing a finding
|
|
241
|
+
|
|
242
|
+
For genuine exceptions only, and the reason matters more than the mechanism:
|
|
243
|
+
|
|
244
|
+
- `copy-ok` (or `craft-ok`) on the line or the line above: a one-off. Listed
|
|
245
|
+
in every report, never hidden.
|
|
246
|
+
- `.copyauditignore` at the repo root: globs, matched on the repo-relative
|
|
247
|
+
path and the bare filename.
|
|
248
|
+
- `.nocopycheck` at the repo root: skips the repo.
|
|
249
|
+
- An `exceptions` entry in `art-direction.json`, with a `because` of at least
|
|
250
|
+
a sentence.
|
|
251
|
+
|
|
252
|
+
Reaching for these more than occasionally means a rule is miscalibrated. Fix
|
|
253
|
+
it here and in craft, not in forty repos.
|
|
254
|
+
|
|
255
|
+
## Rewriting without losing facts
|
|
256
|
+
|
|
257
|
+
A rewrite that removes every tell and drops the price is worse than the
|
|
258
|
+
draft. Before handing over any rewrite, compare it with the original:
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
craft copy compare original.md rewrite.md
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
It lists every protected fact (prices, numbers, dates, times, phone numbers,
|
|
265
|
+
emails, links, postcodes, names) the rewrite **lost**, and every one it
|
|
266
|
+
**added**. Exit 1 if either list is not empty. Restore a lost fact or say why
|
|
267
|
+
it went; source an added fact or remove it, because an unsourced number is an
|
|
268
|
+
invented one.
|
|
269
|
+
|
|
270
|
+
## Changing a rule
|
|
271
|
+
|
|
272
|
+
1. Edit this file.
|
|
273
|
+
2. Add or change the tell in `src/character/tells/`, with a flag fixture per
|
|
274
|
+
detection path and a pass fixture that is real persuasive copy. A tell that
|
|
275
|
+
fires on good sales copy does not ship.
|
|
276
|
+
3. Place it in `src/character/house.ts`. New tells start as `review`. A tell
|
|
277
|
+
moves to `block` only after its hits across the estate have been read by a
|
|
278
|
+
person and the baseline is recorded in `ROADMAP.md`.
|
|
279
|
+
4. `pnpm --filter @domandigital/craft test`. The binding test fails if a
|
|
280
|
+
phrase in this file's blocking tier does not block.
|
package/README.md
CHANGED
|
@@ -94,6 +94,18 @@ reason, in `art-direction.json`:
|
|
|
94
94
|
{ "exceptions": [{ "tell": "ai-violet", "because": "Violet is on the van, the cards and the fascia." }] }
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
### Copy
|
|
98
|
+
|
|
99
|
+
[COPY.md](./COPY.md) is the house copy standard: the blocking tier, the review
|
|
100
|
+
tier, the density tier that reads whole documents, and what to write instead.
|
|
101
|
+
`craft copy --gate` applies the house policy, so the blocking tier fails the
|
|
102
|
+
run. A test fails if a phrase the standard blocks does not block.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npx craft copy --gate content # the house gate: exit 1 on the blocking tier
|
|
106
|
+
npx craft copy compare draft.md rewrite.md # facts a rewrite lost or added
|
|
107
|
+
```
|
|
108
|
+
|
|
97
109
|
The same checks run in code:
|
|
98
110
|
|
|
99
111
|
```ts
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
audit,
|
|
3
|
+
collectInPage,
|
|
4
|
+
loadPlaywright,
|
|
5
|
+
overlayInPage,
|
|
6
|
+
snapshotPage,
|
|
7
|
+
snapshotUrl,
|
|
8
|
+
snapshotUrls
|
|
9
|
+
} from "./chunk-Q4MN3OXF.js";
|
|
10
|
+
import "./chunk-OGSYVCMU.js";
|
|
11
|
+
import "./chunk-CZHRNPDD.js";
|
|
12
|
+
export {
|
|
13
|
+
audit,
|
|
14
|
+
collectInPage,
|
|
15
|
+
loadPlaywright,
|
|
16
|
+
overlayInPage,
|
|
17
|
+
snapshotPage,
|
|
18
|
+
snapshotUrl,
|
|
19
|
+
snapshotUrls
|
|
20
|
+
};
|