@domandigital/craft 0.23.1 → 0.24.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 +17 -5
- package/CHARACTER.md +98 -97
- package/COPY.md +3 -2
- package/PRINCIPLES.md +5 -5
- package/README.md +8 -8
- package/REGISTERS.md +3 -3
- package/design-review.schema.json +1 -1
- package/dist/{audit-DESBVHQJ.js → audit-Q73WGKMS.js} +3 -3
- package/dist/audit.cjs +88 -23
- package/dist/audit.js +88 -23
- package/dist/{chunk-GA3AJN67.js → chunk-2CCL5CO7.js} +17 -15
- package/dist/{chunk-M5QSMIT6.js → chunk-4HPAMB52.js} +4 -4
- package/dist/{chunk-LRWKKJIW.js → chunk-6TOY3FAT.js} +5 -5
- package/dist/{chunk-GDDBCHQR.js → chunk-GJZ4Y5PT.js} +5 -5
- package/dist/{chunk-ZV3FPQPM.js → chunk-OA2MBUJJ.js} +1 -1
- package/dist/{chunk-V3GJSISR.js → chunk-OOT4QLO3.js} +14 -14
- package/dist/{chunk-7DPB5G4G.js → chunk-PXJKYMXF.js} +2 -2
- package/dist/{chunk-EHMEMSM2.js → chunk-R4KPE4G5.js} +72 -9
- package/dist/{cli-FCFRPPHZ.js → cli-766R2BUR.js} +5 -5
- package/dist/{cli-3PFEOEQI.js → cli-CE7ENNNG.js} +6 -6
- package/dist/{cli-SOH4QMMW.js → cli-DTJOOTNM.js} +3 -3
- package/dist/{cli-36YQA6BP.js → cli-EQJITFVE.js} +4 -4
- package/dist/{cli-RXXGSKD6.js → cli-SD2BJSGP.js} +6 -6
- package/dist/{cli-TP5ZMJOL.js → cli-TBBXUECM.js} +5 -5
- package/dist/{cli-YOSHHLIV.js → cli-TV2IJ5TV.js} +2 -3
- package/dist/{cli-7IBKMUMM.js → cli-U3SPFILJ.js} +9 -9
- package/dist/cli.js +3 -3
- package/dist/index.cjs +89 -25
- package/dist/index.d.cts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +89 -25
- package/dist/{viewports-cli-ET664I2Y.js → viewports-cli-SOOHAD5D.js} +3 -3
- package/package.json +1 -1
- package/word-lists.json +1 -1
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
parseFile,
|
|
10
10
|
sentencesOf,
|
|
11
11
|
specificity
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-R4KPE4G5.js";
|
|
13
13
|
import {
|
|
14
14
|
SNAPSHOT_VERSION
|
|
15
15
|
} from "./chunk-QYBAVU3P.js";
|
|
@@ -1170,7 +1170,7 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
1170
1170
|
generation: 1,
|
|
1171
1171
|
severity: "warn",
|
|
1172
1172
|
surface: "source",
|
|
1173
|
-
why: "When every block fades up on scroll, the motion stops meaning anything and the page feels slow.
|
|
1173
|
+
why: "When every block fades up on scroll, the motion stops meaning anything and the page feels slow. One salon client's site had 70, the studio's own 40, a therapy practice's 27.",
|
|
1174
1174
|
fix: `Keep one gated signature moment per page (STANDARD section 7) and let the rest arrive still. The limit here is ${REVEAL_LIMIT} per scan.`,
|
|
1175
1175
|
detect: revealHits,
|
|
1176
1176
|
fixtures: {
|
|
@@ -1186,7 +1186,7 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
1186
1186
|
generation: 1,
|
|
1187
1187
|
severity: "warn",
|
|
1188
1188
|
surface: "source",
|
|
1189
|
-
why: "rounded-full on every button, badge and tag is the framework's friendliest default.
|
|
1189
|
+
why: "rounded-full on every button, badge and tag is the framework's friendliest default. One salon client's site had 162, the studio's own 125. A shape used everywhere is not a shape language.",
|
|
1190
1190
|
fix: `Choose the shape language on purpose (and record it), then use the pill only where it means something. The limit here is ${PILL_LIMIT} per scan.`,
|
|
1191
1191
|
detect: pillHits,
|
|
1192
1192
|
fixtures: {
|
|
@@ -1201,7 +1201,7 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
1201
1201
|
generation: 1,
|
|
1202
1202
|
severity: "warn",
|
|
1203
1203
|
surface: "source",
|
|
1204
|
-
why: "A generator exports the whole shadcn/ui kit whether the site uses it or not.
|
|
1204
|
+
why: "A generator exports the whole shadcn/ui kit whether the site uses it or not. One client's journal site ships a full set under its own book, vine and petal assets.",
|
|
1205
1205
|
fix: "Delete the primitives nothing imports. Restyle the ones that stay so they belong to this site.",
|
|
1206
1206
|
detect: shadcnHits,
|
|
1207
1207
|
fixtures: {
|
|
@@ -1259,7 +1259,7 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
1259
1259
|
f("components/Hero.tsx", `<span className="font-serif italic">reimagined</span>`),
|
|
1260
1260
|
f("app/globals.css", `.hero h1 { font-style: italic; }`)
|
|
1261
1261
|
],
|
|
1262
|
-
pass: [f("components/Hero.tsx", `<h1 className="font-display text-6xl">Nails in
|
|
1262
|
+
pass: [f("components/Hero.tsx", `<h1 className="font-display text-6xl">Nails in Kettering</h1>
|
|
1263
1263
|
<p><em>Walk-ins</em> welcome.</p>`)]
|
|
1264
1264
|
}
|
|
1265
1265
|
},
|
|
@@ -1275,11 +1275,11 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
1275
1275
|
fixtures: {
|
|
1276
1276
|
flag: [
|
|
1277
1277
|
f("components/Hero.tsx", `<span className="inline-flex rounded-full border px-3 py-1 text-xs">Now booking</span>
|
|
1278
|
-
<h1>Nails in
|
|
1278
|
+
<h1>Nails in Kettering</h1>`),
|
|
1279
1279
|
f("components/Hero.tsx", `<span className={cn("rounded-full px-3 text-xs", "bg-rose-500/10")}>New</span>
|
|
1280
1280
|
<h1>Nails</h1>`)
|
|
1281
1281
|
],
|
|
1282
|
-
pass: [f("components/Hero.tsx", `<h1>Nails in
|
|
1282
|
+
pass: [f("components/Hero.tsx", `<h1>Nails in Kettering</h1>
|
|
1283
1283
|
<span className="inline-flex rounded-full border px-3 py-1 text-xs">Tag</span>`)]
|
|
1284
1284
|
}
|
|
1285
1285
|
},
|
|
@@ -1294,7 +1294,7 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
1294
1294
|
detect: heroCtaPairHits,
|
|
1295
1295
|
fixtures: {
|
|
1296
1296
|
flag: [
|
|
1297
|
-
//
|
|
1297
|
+
// A reviewer's #5 and #36 (Pastel 986559): the studio's own hero and process close.
|
|
1298
1298
|
f(
|
|
1299
1299
|
"src/components/Hero.astro",
|
|
1300
1300
|
`<section class="hero">
|
|
@@ -1303,20 +1303,20 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
1303
1303
|
<a href="/work" class="btn btn-ghost">See our work</a>
|
|
1304
1304
|
</section>`
|
|
1305
1305
|
),
|
|
1306
|
-
// The null build's hero, verbatim in shape (
|
|
1306
|
+
// The null build's hero, verbatim in shape (an electrician brief's null pages, in the private dd-craft-corpus).
|
|
1307
1307
|
f(
|
|
1308
1308
|
"index.html",
|
|
1309
1309
|
`<nav><a class="btn" href="#contact">Quote</a></nav>
|
|
1310
1310
|
<section id="hero" class="hero">
|
|
1311
|
-
<h1>Electricians in
|
|
1311
|
+
<h1>Electricians in Reading</h1>
|
|
1312
1312
|
<a href="#contact" class="btn btn-primary">Book a Free Quote</a>
|
|
1313
|
-
<a href="tel:+
|
|
1313
|
+
<a href="tel:+441632960210" class="btn btn-outline">Call 01632 960 210</a>
|
|
1314
1314
|
</section>`
|
|
1315
1315
|
),
|
|
1316
1316
|
f(
|
|
1317
1317
|
"app/page.tsx",
|
|
1318
1318
|
`<section className="relative py-24">
|
|
1319
|
-
<h1 className="text-5xl">Nails in
|
|
1319
|
+
<h1 className="text-5xl">Nails in Kettering</h1>
|
|
1320
1320
|
<div className="flex gap-4">
|
|
1321
1321
|
<Button asChild><a href="/book">Book now</a></Button>
|
|
1322
1322
|
<Button variant="outline">View treatments</Button>
|
|
@@ -1345,7 +1345,7 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
1345
1345
|
</header>`
|
|
1346
1346
|
),
|
|
1347
1347
|
f("app/page.tsx", `<section className="py-24">
|
|
1348
|
-
<h1 className="text-5xl">Nails in
|
|
1348
|
+
<h1 className="text-5xl">Nails in Kettering</h1>
|
|
1349
1349
|
<a className="rounded bg-ink px-5 py-3" href="/book">Book now</a>
|
|
1350
1350
|
</section>
|
|
1351
1351
|
<section><a className="rounded bg-ink px-5 py-3" href="/prices">Prices</a></section>`)
|
|
@@ -2566,13 +2566,15 @@ var HOUSE = {
|
|
|
2566
2566
|
"stock-email-line": { tier: "review", label: "stock email line, start with the reason for writing and end with the next step" },
|
|
2567
2567
|
// New 2026-10-19 (founder): review first, like every new tell, until estate hits are read.
|
|
2568
2568
|
"matey-register": { tier: "review", label: "matey register, write plain and complete British English" },
|
|
2569
|
-
// New 2026-10-20 (founder, from
|
|
2569
|
+
// New 2026-10-20 (founder, from a reviewer's notes): review first, like every new tell.
|
|
2570
2570
|
"confession-line": { tier: "review", label: "confession line, scope the work by saying precisely what is true" },
|
|
2571
2571
|
// New 2026-10-23 (a 92EL report passed the gate with three): review, because a
|
|
2572
2572
|
// genuine disclaimer has the same shape. The gerund pair joined not-just-but.
|
|
2573
2573
|
// New 2026-10-23 (founder, from 92EL deliverables): review, since a short slide label is often fine.
|
|
2574
2574
|
"parallel-pair": { tier: "review", label: "verbless parallel pair, write the sentence it stands for" },
|
|
2575
2575
|
"trailing-contrast": { tier: "review", label: "trailing contrast. Say the positive claim, unless it is a disclaimer the reader needs" },
|
|
2576
|
+
// New 2026-10-26 (founder, from a 92EL deck): review first, like every new tell, until estate hits are read.
|
|
2577
|
+
"absolute-negation": { tier: "review", label: "absolute negation fragment. Say what happens, as a sentence with a subject and a verb" },
|
|
2576
2578
|
"ai-vocabulary": { tier: "review", label: "AI vocabulary" },
|
|
2577
2579
|
"stock-phrase": { tier: "review", label: "stock phrase" },
|
|
2578
2580
|
"hollow-imperative": { tier: "review", label: "hollow imperative" },
|
|
@@ -3195,7 +3197,7 @@ var IMAGERY_TELLS = [
|
|
|
3195
3197
|
];
|
|
3196
3198
|
|
|
3197
3199
|
// src/character/check.ts
|
|
3198
|
-
var CATALOGUE_VERSION = "2026.10.
|
|
3200
|
+
var CATALOGUE_VERSION = "2026.10.9";
|
|
3199
3201
|
var CATALOGUE = [
|
|
3200
3202
|
...[...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map((t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t),
|
|
3201
3203
|
...SPECIFICITY_COPY_TELLS,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
snapshotUrl,
|
|
3
3
|
snapshotUrls
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-OA2MBUJJ.js";
|
|
5
5
|
import {
|
|
6
6
|
describeTypicality,
|
|
7
7
|
loadNull
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-GJZ4Y5PT.js";
|
|
9
9
|
import {
|
|
10
10
|
SOURCE_FILE,
|
|
11
11
|
finish,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
parseFlags,
|
|
15
15
|
prepareReport,
|
|
16
16
|
readPaths
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-OOT4QLO3.js";
|
|
18
18
|
import {
|
|
19
19
|
toJson
|
|
20
20
|
} from "./chunk-QIEDBL4J.js";
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
rhythmOf,
|
|
29
29
|
scanSource,
|
|
30
30
|
typicality
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-2CCL5CO7.js";
|
|
32
32
|
|
|
33
33
|
// src/audit/cli.ts
|
|
34
34
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
formatDirectionMatches,
|
|
5
5
|
summariseDirection,
|
|
6
6
|
validateDirection
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-PXJKYMXF.js";
|
|
8
8
|
import {
|
|
9
9
|
loadNull
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-GJZ4Y5PT.js";
|
|
11
11
|
import {
|
|
12
12
|
parseFlags
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-OOT4QLO3.js";
|
|
14
14
|
import {
|
|
15
15
|
toJson
|
|
16
16
|
} from "./chunk-QIEDBL4J.js";
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
rhythmOf,
|
|
31
31
|
shapeName,
|
|
32
32
|
temperatureName
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-2CCL5CO7.js";
|
|
34
34
|
|
|
35
35
|
// src/estate/cli.ts
|
|
36
36
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -133,7 +133,7 @@ async function snapshotOf(target, cwd) {
|
|
|
133
133
|
return snap;
|
|
134
134
|
}
|
|
135
135
|
if (!/^https?:\/\//.test(target)) throw new Error(`${target} is neither a URL, a snapshot file, nor a site in the register`);
|
|
136
|
-
const { snapshotUrl } = await import("./audit-
|
|
136
|
+
const { snapshotUrl } = await import("./audit-Q73WGKMS.js");
|
|
137
137
|
return snapshotUrl(target);
|
|
138
138
|
}
|
|
139
139
|
var line = (distance, sibling, shared) => `${distance.toFixed(2)}${sibling ? " SIBLING" : ""}${shared.length ? ` shares ${shared.join(", ")}` : ""}`;
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-OQXTQQAU.js";
|
|
4
4
|
import {
|
|
5
5
|
parseFlags
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OOT4QLO3.js";
|
|
7
7
|
import {
|
|
8
8
|
toJson
|
|
9
9
|
} from "./chunk-QIEDBL4J.js";
|
|
@@ -22,10 +22,10 @@ import {
|
|
|
22
22
|
hueSwatch,
|
|
23
23
|
nullPrompt,
|
|
24
24
|
scanSource
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-2CCL5CO7.js";
|
|
26
26
|
import {
|
|
27
27
|
extractStrings
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-R4KPE4G5.js";
|
|
29
29
|
|
|
30
30
|
// src/null/cli.ts
|
|
31
31
|
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
|
|
@@ -120,7 +120,7 @@ async function build(flags, io) {
|
|
|
120
120
|
const toShoot = ids.filter((id) => existsSync(join(pages, `${id}.html`)) && !existsSync(join(shots, `${id}.json`)));
|
|
121
121
|
if (toShoot.length) {
|
|
122
122
|
io.out(`craft null: snapshotting ${toShoot.length} page${toShoot.length === 1 ? "" : "s"}`);
|
|
123
|
-
const { snapshotUrls } = await import("./audit-
|
|
123
|
+
const { snapshotUrls } = await import("./audit-Q73WGKMS.js");
|
|
124
124
|
const results = await snapshotUrls(toShoot.map((id) => pathToFileURL(join(pages, `${id}.html`)).href));
|
|
125
125
|
results.forEach((r, i) => {
|
|
126
126
|
const id = toShoot[i];
|
|
@@ -241,7 +241,7 @@ async function importPages(flags, io) {
|
|
|
241
241
|
const toShoot = pages.filter((p) => !existsSync(join(shots, `${p.id}.json`)));
|
|
242
242
|
if (toShoot.length) {
|
|
243
243
|
io.out(`craft null: snapshotting ${toShoot.length} page${toShoot.length === 1 ? "" : "s"} from ${flags.builder}`);
|
|
244
|
-
const { snapshotUrls } = await import("./audit-
|
|
244
|
+
const { snapshotUrls } = await import("./audit-Q73WGKMS.js");
|
|
245
245
|
const servers = /* @__PURE__ */ new Map();
|
|
246
246
|
try {
|
|
247
247
|
for (const p of toShoot) if (!servers.has(p.root)) servers.set(p.root, await serveDir(p.root));
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
holdHouseBlocks,
|
|
11
11
|
houseRule,
|
|
12
12
|
scanSource
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-2CCL5CO7.js";
|
|
14
14
|
import {
|
|
15
15
|
compareCompetitor,
|
|
16
16
|
compareFacts,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
pageCopy,
|
|
22
22
|
protectedFacts,
|
|
23
23
|
visibleText
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-R4KPE4G5.js";
|
|
25
25
|
|
|
26
26
|
// src/character/config.ts
|
|
27
27
|
var DEFAULT_IGNORE = [".claude", ".agents", ".cursor"];
|
|
@@ -622,7 +622,7 @@ async function pageSnapshot(target, cwd) {
|
|
|
622
622
|
return readSnapshot(JSON.parse(readFileSync(local, "utf8")), target);
|
|
623
623
|
}
|
|
624
624
|
if (!/^https?:\/\//.test(target)) throw new Error(`${target} is neither a URL nor a snapshot file`);
|
|
625
|
-
const { snapshotUrl } = await import("./audit-
|
|
625
|
+
const { snapshotUrl } = await import("./audit-Q73WGKMS.js");
|
|
626
626
|
return snapshotUrl(target);
|
|
627
627
|
}
|
|
628
628
|
async function briefFor(flags, cwd) {
|
|
@@ -633,7 +633,7 @@ async function briefFor(flags, cwd) {
|
|
|
633
633
|
if (typeof brief === "string" && brief.trim()) return brief;
|
|
634
634
|
} else if (flags.direction) throw new Error(`no such file: ${flags.direction}`);
|
|
635
635
|
if (flags.null) {
|
|
636
|
-
const { loadNull } = await import("./cli-
|
|
636
|
+
const { loadNull } = await import("./cli-EQJITFVE.js");
|
|
637
637
|
return loadNull(flags.null, cwd).brief;
|
|
638
638
|
}
|
|
639
639
|
return void 0;
|
|
@@ -659,37 +659,37 @@ async function compareWithCompetitor(flags, io) {
|
|
|
659
659
|
function run(argv, io) {
|
|
660
660
|
const [command, ...rest] = argv;
|
|
661
661
|
if (command === "direction") {
|
|
662
|
-
return import("./cli-
|
|
662
|
+
return import("./cli-TBBXUECM.js").then((m) => m.runDirection(rest, io));
|
|
663
663
|
}
|
|
664
664
|
if (command === "report" || command === "retrofit") {
|
|
665
|
-
return import("./cli-
|
|
665
|
+
return import("./cli-U3SPFILJ.js").then((m) => m.runReport(command, rest, io));
|
|
666
666
|
}
|
|
667
667
|
if (command === "register") {
|
|
668
|
-
return import("./cli-
|
|
668
|
+
return import("./cli-TV2IJ5TV.js").then((m) => m.runRegister(rest, io));
|
|
669
669
|
}
|
|
670
670
|
if (command === "estate") {
|
|
671
|
-
return import("./cli-
|
|
671
|
+
return import("./cli-SD2BJSGP.js").then((m) => m.runEstate(rest, io));
|
|
672
672
|
}
|
|
673
673
|
if (command === "null") {
|
|
674
|
-
return import("./cli-
|
|
674
|
+
return import("./cli-EQJITFVE.js").then((m) => m.runNull(rest, io));
|
|
675
675
|
}
|
|
676
676
|
if (command === "calibrate") {
|
|
677
|
-
return import("./cli-
|
|
677
|
+
return import("./cli-766R2BUR.js").then((m) => m.runCalibrate(rest, io));
|
|
678
678
|
}
|
|
679
679
|
if (command === "tells" && rest[0] === "harvest") {
|
|
680
|
-
return import("./cli-
|
|
680
|
+
return import("./cli-EQJITFVE.js").then((m) => m.runHarvest(rest, io)).catch((error) => {
|
|
681
681
|
io.err(`craft: ${error.message}`);
|
|
682
682
|
return 2;
|
|
683
683
|
});
|
|
684
684
|
}
|
|
685
685
|
if (command === "phone-first") {
|
|
686
|
-
return import("./cli-
|
|
686
|
+
return import("./cli-DTJOOTNM.js").then((m) => m.runPhoneFirst(rest, io));
|
|
687
687
|
}
|
|
688
688
|
if (command === "viewports") {
|
|
689
|
-
return import("./viewports-cli-
|
|
689
|
+
return import("./viewports-cli-SOOHAD5D.js").then((m) => m.runViewports(rest, io));
|
|
690
690
|
}
|
|
691
691
|
if (command === "snapshot" || command === "audit") {
|
|
692
|
-
return import("./cli-
|
|
692
|
+
return import("./cli-CE7ENNNG.js").then((m) => m.runAudit(command, rest, io));
|
|
693
693
|
}
|
|
694
694
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
695
695
|
io.out(HELP);
|
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
levenshtein,
|
|
11
11
|
normaliseFamily,
|
|
12
12
|
parseColour
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-2CCL5CO7.js";
|
|
14
14
|
import {
|
|
15
15
|
TRADE_NOUNS
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-R4KPE4G5.js";
|
|
17
17
|
|
|
18
18
|
// src/direction/types.ts
|
|
19
19
|
var DIRECTION_VERSION = 2;
|
|
@@ -746,6 +746,29 @@ var PARALLEL_PAIR = [
|
|
|
746
746
|
// a verbless list as a tagline: "Service account, match-day reminders, analytics."
|
|
747
747
|
/^[ \t]*(?:#{1,6}[ \t]+)?[A-Z][\w'’-]*(?:[ \t]+(?!(?:and|or)\b)[\w'’-]+){0,2}(?:,[ \t]+(?!(?:and|or)\b)[\w'’-]+(?:[ \t]+(?!(?:and|or)\b)[\w'’-]+){0,2}){2,3}\.[ \t]*$/m
|
|
748
748
|
];
|
|
749
|
+
var CLAUSE_START = String.raw`(?<=^[ \t]*(?:[-*][ \t]+|#{1,6}[ \t]+)?|[;:,(—–][ \t]*|[.!?][ \t]+)`;
|
|
750
|
+
var ABSOLUTE_NEGATION = new RegExp(
|
|
751
|
+
`${CLAUSE_START}(?:never|nothing|nobody|no[ \\t]+one|no)(?:[ \\t]+[\\w'\u2019\xA3$\u20AC-]+){1,6}(?=[ \\t]*(?:[.,;:!?)"\u201D\u2014\u2013]|$))`,
|
|
752
|
+
"im"
|
|
753
|
+
);
|
|
754
|
+
var FINITE = /^(?:is|are|was|were|am|be|been|being|has|have|had|do|does|did|can|could|will|would|shall|should|may|might|must|need|needs|gets|got|goes|went|comes|came|makes|means|matters|happens|stays|changes|works|takes|says|knows|seems|feels|looks|leaves|left|appears|uses|runs|shows|sends|holds|reads|writes|opens|stores|reaches|applies|isn['’]t|aren['’]t|wasn['’]t)$/i;
|
|
755
|
+
var NO_IDIOM = /^(?:date|longer|later|sooner|matter|more|less|fewer|other|one|doubt|wonder|thanks|problem)$/i;
|
|
756
|
+
var PARTICIPLE = /^[\w-]*(?:ed|en|wn|ld|lt|nt|pt|ne|id)$/i;
|
|
757
|
+
function notAbsoluteFragment(block, match, index) {
|
|
758
|
+
const [head, ...rest] = match.trim().split(/[ \t]+/);
|
|
759
|
+
let words = rest;
|
|
760
|
+
const lead = head.toLowerCase();
|
|
761
|
+
if (lead === "no" && words[0]?.toLowerCase() === "one") words = words.slice(1);
|
|
762
|
+
else if (lead === "no" && NO_IDIOM.test(words[0] ?? "")) return true;
|
|
763
|
+
if (words.some((w) => FINITE.test(w) || /['’](?:s|re|ve|ll|d)$/i.test(w))) return true;
|
|
764
|
+
if (lead === "never" && !PARTICIPLE.test(words[0] ?? "")) return true;
|
|
765
|
+
if (lead !== "no" && lead !== "never" && /^(?!else$|less$|across$)[a-z]+s$/i.test(words[0] ?? "")) return true;
|
|
766
|
+
const sentence = lineAround(block.text, index);
|
|
767
|
+
const line = sentence.trim().replace(/^[-*]\s+/, "");
|
|
768
|
+
if (NO_X_NO_Y.test(sentence) || NO_X_BADGE.test(line)) return true;
|
|
769
|
+
if (NEGATIVE_REASSURANCE.some((p) => wordList([p]).test(match))) return true;
|
|
770
|
+
return emptyState(block, match, index);
|
|
771
|
+
}
|
|
749
772
|
var alreadyBlocked = (block, match, index) => {
|
|
750
773
|
const before = block.text.slice(0, index);
|
|
751
774
|
const start = Math.max(before.search(/[^.!?\n]*$/), 0);
|
|
@@ -872,7 +895,7 @@ var COPY_TELLS = [
|
|
|
872
895
|
fixtures: {
|
|
873
896
|
flag: [
|
|
874
897
|
f("content/home.md", "Your options, in plain English."),
|
|
875
|
-
//
|
|
898
|
+
// A reviewer's draft lines, Pastel 986559 #12 and #13 (the diagnosis taken, the wording not).
|
|
876
899
|
f("content/clinics.md", "Tell us what's happening and we'll say plainly what we would take on."),
|
|
877
900
|
f("content/pricing.md", "DD's payment terms, plainly stated."),
|
|
878
901
|
f("content/clinics.md", "We explain our findings in plain terms."),
|
|
@@ -998,7 +1021,7 @@ var COPY_TELLS = [
|
|
|
998
1021
|
fixtures: {
|
|
999
1022
|
flag: [
|
|
1000
1023
|
// From the portal's seeded prospect follow-ups, 2026-10-19.
|
|
1001
|
-
f("emails/followup.md", "Alright Dave, it's
|
|
1024
|
+
f("emails/followup.md", "Alright Dave, it's Sam. No pressure, have a look when you get a min."),
|
|
1002
1025
|
f("emails/followup.md", "All good if you're not fussed mate, I'll leave it there."),
|
|
1003
1026
|
f("emails/followup.md", "Carlo, built the shop a proper site."),
|
|
1004
1027
|
f("content/contact.md", "No worries, we'll get you sorted."),
|
|
@@ -1011,11 +1034,11 @@ var COPY_TELLS = [
|
|
|
1011
1034
|
f("content/home.md", "Proper planning saves a second visit."),
|
|
1012
1035
|
f("content/certificates.md", "Download a sorted list of certificates, sorted by date."),
|
|
1013
1036
|
f("content/directions.md", "At the junction, take the right then left turn."),
|
|
1014
|
-
//
|
|
1037
|
+
// A barber client's own approved line, and "mate" as a noun.
|
|
1015
1038
|
f("content/home.md", "Every cut starts with a proper chat about what you actually want."),
|
|
1016
1039
|
f("content/home.md", "It feels like getting a cut from a mate who knows what he is doing."),
|
|
1017
1040
|
f("emails/quote.md", "Have a look at the quote and ring me if Thursday does not work."),
|
|
1018
|
-
f("content/contact.md", "Give us a ring on
|
|
1041
|
+
f("content/contact.md", "Give us a ring on 01632 960123. We take a look at the fault before quoting."),
|
|
1019
1042
|
f("content/home.md", "Built to last, and made for older houses.")
|
|
1020
1043
|
]
|
|
1021
1044
|
}
|
|
@@ -1027,14 +1050,14 @@ var COPY_TELLS = [
|
|
|
1027
1050
|
severity: "warn",
|
|
1028
1051
|
surface: "copy",
|
|
1029
1052
|
why: "'What this is and is not', 'we have not yet built', 'our experience so far is one practice', 'what we would and wouldn't take on': a model scopes a business by confessing its limits. It reads as small print, and it turns a buyer away before they make contact.",
|
|
1030
|
-
fix: "Scope by being precise about what is true, and leave the door open: 'Built and run by us for
|
|
1053
|
+
fix: "Scope by being precise about what is true, and leave the door open: 'Built and run by us for a children's therapy practice whose clinicians work across several sites.' Keep contractual facts (eligibility, who runs what) and say them as what the buyer gets.",
|
|
1031
1054
|
detect: phrases(CONFESSION, quoted),
|
|
1032
1055
|
fixtures: {
|
|
1033
1056
|
flag: [
|
|
1034
|
-
// Each one from the
|
|
1057
|
+
// Each one from the studio's own site, as a reviewer flagged it (Pastel 986559 #8, #12, #13).
|
|
1035
1058
|
f("content/clinics.md", "What this is and is not: a practice portal we built and run for one practice."),
|
|
1036
1059
|
f("content/clinics.md", "We have not yet built and run the digital systems of a practice with several clinicians."),
|
|
1037
|
-
f("content/home.md", "Including the client portal we run for
|
|
1060
|
+
f("content/home.md", "Including the client portal we run for a therapy practice. Our clinic experience so far is one practice."),
|
|
1038
1061
|
f("content/clinics.md", "Tell us what's happening and we'll say what we would and wouldn't take on."),
|
|
1039
1062
|
f("content/clinics.md", "We will say what carries over from this work and what we would need to learn first."),
|
|
1040
1063
|
f("content/clinics.md", "We will tell you where our experience applies directly and where it is close."),
|
|
@@ -1043,8 +1066,8 @@ var COPY_TELLS = [
|
|
|
1043
1066
|
f("content/mobile.md", "Mobile is our newest line, so we tell you what we have and haven't done.")
|
|
1044
1067
|
],
|
|
1045
1068
|
pass: [
|
|
1046
|
-
f("content/clinics.md", "Built and run by us for
|
|
1047
|
-
f("content/home.md", "The client portal we run for
|
|
1069
|
+
f("content/clinics.md", "Built and run by us for a therapy practice, a growing one whose clinicians work remotely."),
|
|
1070
|
+
f("content/home.md", "The client portal we run for a children's therapy practice."),
|
|
1048
1071
|
// Contractual facts, said as they are.
|
|
1049
1072
|
f("content/pricing.md", "Pay monthly is open to UK residents aged 18 and over, subject to status."),
|
|
1050
1073
|
f("content/pricing.md", "Your monthly payments do not buy the site; the buy-out price does."),
|
|
@@ -1284,6 +1307,46 @@ var COPY_TELLS = [
|
|
|
1284
1307
|
]
|
|
1285
1308
|
}
|
|
1286
1309
|
},
|
|
1310
|
+
{
|
|
1311
|
+
id: "absolute-negation",
|
|
1312
|
+
name: "Absolute negation fragment",
|
|
1313
|
+
generation: 2,
|
|
1314
|
+
severity: "warn",
|
|
1315
|
+
surface: "copy",
|
|
1316
|
+
why: "'never pre-ticked', 'no servers', 'nothing to buy or run': a verbless fragment that opens on never, nothing, nobody or 'no' + noun. Read aloud it sounds rigid, it promises an absolute the copy rarely means, and it defines the thing by what it is not.",
|
|
1317
|
+
fix: "Say what happens, as a sentence: 'A guardian ticks the consent box in the email.', 'Microsoft runs the servers.' Keep the absolute only where it is literally true and the reader needs it, and give it a subject and a verb.",
|
|
1318
|
+
detect: phrases([ABSOLUTE_NEGATION], quoted, allText, notAbsoluteFragment),
|
|
1319
|
+
fixtures: {
|
|
1320
|
+
flag: [
|
|
1321
|
+
// From a 92EL deck that passed the gate, October 2026.
|
|
1322
|
+
f("content/deck.md", "A guardian consents by email; never pre-ticked."),
|
|
1323
|
+
f("content/deck.md", "Pay per user per month; no servers."),
|
|
1324
|
+
f("content/deck.md", "Microsoft runs it, so there is less to manage: nothing to buy or run."),
|
|
1325
|
+
f("content/deck.md", "Players register on their phone. Nothing on-site to join."),
|
|
1326
|
+
f("content/deck.md", "Each player is checked against the list once, no duplicates."),
|
|
1327
|
+
f("app/slide.tsx", `export const note = "Nobody to chase for a form.";`)
|
|
1328
|
+
],
|
|
1329
|
+
pass: [
|
|
1330
|
+
f("content/report.md", "Players never open the lists."),
|
|
1331
|
+
f("content/report.md", "Nothing is copied by hand."),
|
|
1332
|
+
f("content/report.md", "It is not tested at national scale."),
|
|
1333
|
+
f("content/report.md", "Never share your password with anyone."),
|
|
1334
|
+
f("content/report.md", "No one knows the password but the club secretary."),
|
|
1335
|
+
// A Harvard citation with no date.
|
|
1336
|
+
f("content/report.md", "Children's data needs a lawful basis (ICO, no date)."),
|
|
1337
|
+
f("content/report.md", "- Information Commissioner's Office (ICO), no date. Children and the UK GDPR."),
|
|
1338
|
+
// A form answer option.
|
|
1339
|
+
f("content/form.md", "Is the player under 18?\n\n- Yes\n- No"),
|
|
1340
|
+
f("app/form.tsx", `export const options = ["Yes", "No"];`),
|
|
1341
|
+
f("content/form.md", "No, the player is 18 or over."),
|
|
1342
|
+
f("content/home.md", "We no longer post paper forms."),
|
|
1343
|
+
f("content/report.md", "If the check fails, nothing new appears in the list."),
|
|
1344
|
+
// The blocking tells report these.
|
|
1345
|
+
f("content/home.md", "We reply within a day. No obligation, no spam."),
|
|
1346
|
+
f("app/list.tsx", `export const empty = "No results found.";`)
|
|
1347
|
+
]
|
|
1348
|
+
}
|
|
1349
|
+
},
|
|
1287
1350
|
{
|
|
1288
1351
|
id: "where-x-meets-y",
|
|
1289
1352
|
name: "'Where X meets Y'",
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
expandNullTargets,
|
|
6
6
|
loadNull
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GJZ4Y5PT.js";
|
|
8
8
|
import "./chunk-OQXTQQAU.js";
|
|
9
9
|
import {
|
|
10
10
|
parseFlags
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-OOT4QLO3.js";
|
|
12
12
|
import {
|
|
13
13
|
toJson
|
|
14
14
|
} from "./chunk-QIEDBL4J.js";
|
|
@@ -21,8 +21,8 @@ import {
|
|
|
21
21
|
fingerprint,
|
|
22
22
|
scanSource,
|
|
23
23
|
typicality
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import "./chunk-
|
|
24
|
+
} from "./chunk-2CCL5CO7.js";
|
|
25
|
+
import "./chunk-R4KPE4G5.js";
|
|
26
26
|
import {
|
|
27
27
|
SNAPSHOT_VERSION
|
|
28
28
|
} from "./chunk-QYBAVU3P.js";
|
|
@@ -188,7 +188,7 @@ async function runCalibrate(args, io) {
|
|
|
188
188
|
if (toTake.length) {
|
|
189
189
|
io.err(`craft calibrate: taking ${toTake.length} snapshot${toTake.length === 1 ? "" : "s"}`);
|
|
190
190
|
try {
|
|
191
|
-
const { snapshotUrls } = await import("./audit-
|
|
191
|
+
const { snapshotUrls } = await import("./audit-Q73WGKMS.js");
|
|
192
192
|
const results = await snapshotUrls(toTake.map((t) => t.url));
|
|
193
193
|
results.forEach((r, i) => {
|
|
194
194
|
const { entry, keep } = toTake[i];
|
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
merge,
|
|
5
5
|
resolvePages,
|
|
6
6
|
runAudit
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-4HPAMB52.js";
|
|
8
|
+
import "./chunk-OA2MBUJJ.js";
|
|
9
9
|
import "./chunk-JYNXGCYZ.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-GJZ4Y5PT.js";
|
|
11
11
|
import "./chunk-OQXTQQAU.js";
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-OOT4QLO3.js";
|
|
13
13
|
import "./chunk-QIEDBL4J.js";
|
|
14
14
|
import "./chunk-WUACK2HX.js";
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-2CCL5CO7.js";
|
|
16
|
+
import "./chunk-R4KPE4G5.js";
|
|
17
17
|
import "./chunk-QYBAVU3P.js";
|
|
18
18
|
export {
|
|
19
19
|
describeComponents,
|
|
@@ -16,12 +16,12 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
SOURCE_FILE,
|
|
18
18
|
readPaths
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-OOT4QLO3.js";
|
|
20
20
|
import {
|
|
21
21
|
toJson
|
|
22
22
|
} from "./chunk-QIEDBL4J.js";
|
|
23
|
-
import "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-2CCL5CO7.js";
|
|
24
|
+
import "./chunk-R4KPE4G5.js";
|
|
25
25
|
import "./chunk-QYBAVU3P.js";
|
|
26
26
|
|
|
27
27
|
// src/phone/cli.ts
|
|
@@ -9,13 +9,13 @@ import {
|
|
|
9
9
|
runHarvest,
|
|
10
10
|
runNull,
|
|
11
11
|
serveDir
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-GJZ4Y5PT.js";
|
|
13
13
|
import "./chunk-OQXTQQAU.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-OOT4QLO3.js";
|
|
15
15
|
import "./chunk-QIEDBL4J.js";
|
|
16
16
|
import "./chunk-WUACK2HX.js";
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-2CCL5CO7.js";
|
|
18
|
+
import "./chunk-R4KPE4G5.js";
|
|
19
19
|
import "./chunk-QYBAVU3P.js";
|
|
20
20
|
export {
|
|
21
21
|
describeTypicality,
|
|
@@ -4,15 +4,15 @@ import {
|
|
|
4
4
|
formatPairs,
|
|
5
5
|
loadEstate,
|
|
6
6
|
runEstate
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-6TOY3FAT.js";
|
|
8
|
+
import "./chunk-PXJKYMXF.js";
|
|
9
|
+
import "./chunk-GJZ4Y5PT.js";
|
|
10
10
|
import "./chunk-OQXTQQAU.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-OOT4QLO3.js";
|
|
12
12
|
import "./chunk-QIEDBL4J.js";
|
|
13
13
|
import "./chunk-WUACK2HX.js";
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-2CCL5CO7.js";
|
|
15
|
+
import "./chunk-R4KPE4G5.js";
|
|
16
16
|
import "./chunk-QYBAVU3P.js";
|
|
17
17
|
export {
|
|
18
18
|
formatComponentPairs,
|
|
@@ -19,10 +19,10 @@ import {
|
|
|
19
19
|
checkReason,
|
|
20
20
|
researchPrompt,
|
|
21
21
|
validateDirection
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-PXJKYMXF.js";
|
|
23
23
|
import {
|
|
24
24
|
parseFlags
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-OOT4QLO3.js";
|
|
26
26
|
import {
|
|
27
27
|
toJson
|
|
28
28
|
} from "./chunk-QIEDBL4J.js";
|
|
@@ -38,8 +38,8 @@ import {
|
|
|
38
38
|
isCream,
|
|
39
39
|
oklchToRgb,
|
|
40
40
|
parseColour
|
|
41
|
-
} from "./chunk-
|
|
42
|
-
import "./chunk-
|
|
41
|
+
} from "./chunk-2CCL5CO7.js";
|
|
42
|
+
import "./chunk-R4KPE4G5.js";
|
|
43
43
|
import "./chunk-QYBAVU3P.js";
|
|
44
44
|
|
|
45
45
|
// src/direction/cli.ts
|
|
@@ -484,7 +484,7 @@ var readJson = (path) => JSON.parse(readFileSync(path, "utf8"));
|
|
|
484
484
|
async function snapshotFrom(target, cwd) {
|
|
485
485
|
if (!target) return void 0;
|
|
486
486
|
if (/^https?:\/\//.test(target)) {
|
|
487
|
-
const { snapshotUrl } = await import("./audit-
|
|
487
|
+
const { snapshotUrl } = await import("./audit-Q73WGKMS.js");
|
|
488
488
|
return snapshotUrl(target);
|
|
489
489
|
}
|
|
490
490
|
return readSnapshot(readJson(resolve(cwd, target)), target);
|