@domandigital/craft 0.14.0 → 0.16.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 +25 -0
- package/CHARACTER.md +247 -28
- package/README.md +19 -1
- package/art-direction.schema.json +428 -25
- package/dist/{audit-6WHR7ZTV.js → audit-UJCUWORL.js} +2 -2
- package/dist/audit.cjs +5 -0
- package/dist/audit.js +5 -0
- package/dist/{chunk-GQVEYTBY.js → chunk-B2MJLGF6.js} +14 -3
- package/dist/{chunk-PBK6SYH5.js → chunk-CVG5GSMN.js} +46 -11
- package/dist/{chunk-XVBR2UPH.js → chunk-LWVGIJ5A.js} +175 -37
- package/dist/{chunk-3OTKMKJQ.js → chunk-LXDNIJFG.js} +36 -15
- package/dist/{cli-WG3UJNUF.js → chunk-NTIPJ5U7.js} +12 -172
- package/dist/chunk-OQXTQQAU.js +27 -0
- package/dist/{chunk-7T5WR74C.js → chunk-TQKDCA4V.js} +4 -4
- package/dist/chunk-WAV6AKR4.js +711 -0
- package/dist/{chunk-Q7ILL2HV.js → chunk-YK2FFL6Z.js} +1 -1
- package/dist/cli-3GQROFCB.js +241 -0
- package/dist/{cli-WSHPSWVU.js → cli-52M62QBN.js} +6 -5
- package/dist/{cli-Z2IQG3LC.js → cli-AVYNHBFJ.js} +12 -5
- package/dist/cli-CWORJYOU.js +190 -0
- package/dist/{cli-O3CDSONO.js → cli-K4LSRYBE.js} +86 -20
- package/dist/{cli-3CGUKXY4.js → cli-PEATEW57.js} +6 -4
- package/dist/cli.js +2 -2
- package/dist/generate.js +6 -0
- package/dist/index.cjs +1142 -208
- package/dist/index.d.cts +603 -57
- package/dist/index.d.ts +603 -57
- package/dist/index.js +1084 -208
- package/package.json +1 -1
- package/dist/chunk-OOZOEWGD.js +0 -295
|
@@ -1,47 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
validateDirection
|
|
3
|
-
} from "./chunk-OOZOEWGD.js";
|
|
4
|
-
import {
|
|
5
|
-
merge
|
|
6
|
-
} from "./chunk-7T5WR74C.js";
|
|
7
|
-
import "./chunk-Q7ILL2HV.js";
|
|
8
|
-
import "./chunk-JYNXGCYZ.js";
|
|
9
|
-
import {
|
|
10
|
-
SIBLING_AT,
|
|
11
|
-
compareToEstate,
|
|
12
|
-
loadEstate
|
|
13
|
-
} from "./chunk-PBK6SYH5.js";
|
|
14
|
-
import {
|
|
15
|
-
loadNull
|
|
16
|
-
} from "./chunk-XVBR2UPH.js";
|
|
17
|
-
import {
|
|
18
|
-
COPY_FILE,
|
|
19
|
-
NOT_COPY_DIR,
|
|
20
|
-
SOURCE_FILE,
|
|
21
|
-
applySeverity,
|
|
22
|
-
loadConfig,
|
|
23
|
-
loadExceptions,
|
|
24
|
-
parseFlags,
|
|
25
|
-
readPaths,
|
|
26
|
-
toJson
|
|
27
|
-
} from "./chunk-3OTKMKJQ.js";
|
|
28
|
-
import {
|
|
29
|
-
readSnapshot
|
|
30
|
-
} from "./chunk-WUACK2HX.js";
|
|
31
|
-
import {
|
|
32
|
-
CATALOGUE,
|
|
33
|
-
auditSnapshot,
|
|
34
|
-
checkCopy,
|
|
35
|
-
fingerprint,
|
|
36
|
-
scanSource,
|
|
37
|
-
typicality
|
|
38
|
-
} from "./chunk-GQVEYTBY.js";
|
|
39
|
-
import "./chunk-QYBAVU3P.js";
|
|
40
|
-
|
|
41
|
-
// src/report/cli.ts
|
|
42
|
-
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
43
|
-
import { dirname, resolve } from "path";
|
|
44
|
-
|
|
45
1
|
// src/report/index.ts
|
|
46
2
|
var TELL_HEAVY = 3;
|
|
47
3
|
var MIN_DECIDED = 5;
|
|
@@ -141,8 +97,16 @@ function characterReport(input, copyTells = /* @__PURE__ */ new Set()) {
|
|
|
141
97
|
}
|
|
142
98
|
if (input.direction) {
|
|
143
99
|
const d = input.direction;
|
|
144
|
-
const
|
|
145
|
-
|
|
100
|
+
const layers = d.layers;
|
|
101
|
+
const raised2 = !d.valid || d.decided < MIN_DECIDED || !layers.complete;
|
|
102
|
+
const pages = layers.hierarchy.map((p) => `${p.page} ${p.decided} of ${p.total}`).join(", ");
|
|
103
|
+
const detail = [
|
|
104
|
+
`job ${layers.job ? "decided" : "not decided"}`,
|
|
105
|
+
`hierarchy ${pages || "not decided"}`,
|
|
106
|
+
`${d.decided} of 7 choices decided with a reason`
|
|
107
|
+
].join("; ");
|
|
108
|
+
signals.push({ id: "reasons", raised: raised2, detail: `${detail}${d.valid ? "" : ", art-direction.json has errors"}` });
|
|
109
|
+
if (!layers.job) actions.push({ area: "direction", what: "Write the job map: what the visitor is trying to get done, from reviews, enquiries and conversations. Run craft direction research for the questions.", why: "structure and look are decided from the job", priority: 1 });
|
|
146
110
|
for (const p of d.problems) {
|
|
147
111
|
const key = p.at.match(/^choices\.(\w+)/)?.[1];
|
|
148
112
|
if (p.severity === "error" || p.message === "not decided yet") {
|
|
@@ -189,131 +153,7 @@ function choiceOfShared(part) {
|
|
|
189
153
|
return /running order/.test(part) ? void 0 : "motif";
|
|
190
154
|
}
|
|
191
155
|
|
|
192
|
-
// src/report/retrofit.ts
|
|
193
|
-
var HEADINGS = {
|
|
194
|
-
direction: "Decide first",
|
|
195
|
-
type: "Type",
|
|
196
|
-
colour: "Colour",
|
|
197
|
-
shape: "Shape",
|
|
198
|
-
effects: "Effects",
|
|
199
|
-
motion: "Motion",
|
|
200
|
-
layout: "How sections look (not where they sit)",
|
|
201
|
-
copy: "Copy"
|
|
202
|
-
};
|
|
203
|
-
var ORDER = ["direction", "type", "colour", "shape", "effects", "motion", "layout", "copy"];
|
|
204
|
-
function target(a, direction) {
|
|
205
|
-
if (!a.choice || a.area === "direction") return "";
|
|
206
|
-
const c = direction?.choices?.[a.choice];
|
|
207
|
-
if (c && c.because && !c.because.startsWith("PROPOSED:")) return `
|
|
208
|
-
- Use: **${c.value}** for ${a.choice}. ${c.because}`;
|
|
209
|
-
return `
|
|
210
|
-
- Waits on: deciding **${a.choice}** in art-direction.json.`;
|
|
211
|
-
}
|
|
212
|
-
function retrofitPlan(report, direction) {
|
|
213
|
-
const lines = [
|
|
214
|
-
`# Retrofit: ${report.site}`,
|
|
215
|
-
"",
|
|
216
|
-
`**Now:** ${report.verdict}. ${report.summary}`,
|
|
217
|
-
"",
|
|
218
|
-
"| Signal | Raised | Reading |",
|
|
219
|
-
"|---|---|---|",
|
|
220
|
-
...report.signals.map((s) => `| ${s.id} | ${s.raised === null ? "not measured" : s.raised ? "yes" : "no"} | ${s.detail} |`),
|
|
221
|
-
"",
|
|
222
|
-
"**Done when** `craft report` says *decided*: fewer than three design tells, typicality under 0.10 against this brief's null, no sibling in the estate, and at least five of the seven choices decided with a reason.",
|
|
223
|
-
""
|
|
224
|
-
];
|
|
225
|
-
let n = 0;
|
|
226
|
-
for (const area of ORDER) {
|
|
227
|
-
const list = report.actions.filter((a) => a.area === area);
|
|
228
|
-
if (list.length === 0) continue;
|
|
229
|
-
n += 1;
|
|
230
|
-
lines.push(`## ${n}. ${HEADINGS[area]}`, "");
|
|
231
|
-
for (const a of list) lines.push(`- [ ] ${a.what} *(${a.why})*${target(a, direction)}`);
|
|
232
|
-
lines.push("");
|
|
233
|
-
}
|
|
234
|
-
if (n === 0) lines.push("Nothing to change.", "");
|
|
235
|
-
lines.push(
|
|
236
|
-
"## Leave alone",
|
|
237
|
-
"",
|
|
238
|
-
"Navigation, the order of the sections, where the call to action sits, reading order and legibility. Change the look, never the page grammar. A layout tell above is about how a section looks: the icon tiles, the chip, the strip. The section stays where it is.",
|
|
239
|
-
""
|
|
240
|
-
);
|
|
241
|
-
return lines.join("\n");
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// src/report/cli.ts
|
|
245
|
-
var USAGE = "usage: craft report|retrofit <url | snapshot.json> [--repo <dir>] [--null <dir>] [--estate <estate.json>] [--direction <file>] [--json] [--out <file>]";
|
|
246
|
-
var sameUrl = (a, b) => a.replace(/\/+$/, "").toLowerCase() === b.replace(/\/+$/, "").toLowerCase();
|
|
247
|
-
function formatCharacter(r) {
|
|
248
|
-
const lines = [`craft report ${r.site}`, "", `Verdict: ${r.verdict.toUpperCase()}. ${r.summary}`, ""];
|
|
249
|
-
for (const s of r.signals) lines.push(` ${s.id.padEnd(11)} ${s.raised === null ? "\xB7" : s.raised ? "RAISED" : "ok"} ${s.detail}`);
|
|
250
|
-
lines.push("", "What to do, in order:");
|
|
251
|
-
r.actions.slice(0, 12).forEach((a, i) => lines.push(` ${String(i + 1).padStart(2)}. [${a.area}] ${a.what}`, ` ${a.why}`));
|
|
252
|
-
if (r.actions.length > 12) lines.push(` ...and ${r.actions.length - 12} more: craft retrofit writes them all.`);
|
|
253
|
-
return lines.join("\n");
|
|
254
|
-
}
|
|
255
|
-
async function runReport(command, args, io) {
|
|
256
|
-
try {
|
|
257
|
-
const flags = parseFlags(args);
|
|
258
|
-
if (typeof flags === "string") throw new Error(flags);
|
|
259
|
-
const target2 = flags.positional[0];
|
|
260
|
-
if (!target2) throw new Error(USAGE);
|
|
261
|
-
const local = resolve(io.cwd, target2);
|
|
262
|
-
let snap;
|
|
263
|
-
if (/\.json$/i.test(target2) && existsSync(local)) {
|
|
264
|
-
snap = readSnapshot(JSON.parse(readFileSync(local, "utf8")), target2);
|
|
265
|
-
} else {
|
|
266
|
-
const { snapshotUrl } = await import("./audit-6WHR7ZTV.js");
|
|
267
|
-
snap = await snapshotUrl(target2);
|
|
268
|
-
}
|
|
269
|
-
const fp = fingerprint(snap);
|
|
270
|
-
const repo = flags.repo ? resolve(io.cwd, flags.repo) : null;
|
|
271
|
-
const exceptions = loadExceptions(flags, repo ?? io.cwd);
|
|
272
|
-
const config = loadConfig(flags, repo ?? io.cwd);
|
|
273
|
-
let findings = auditSnapshot(snap, { exceptions });
|
|
274
|
-
if (repo) {
|
|
275
|
-
findings = merge(scanSource(readPaths(["."], repo, SOURCE_FILE, void 0, config.ignore), { exceptions }), findings);
|
|
276
|
-
const copyPaths = config.copyPaths ?? ["."];
|
|
277
|
-
findings = merge(findings, checkCopy(readPaths(copyPaths, repo, COPY_FILE, (d) => NOT_COPY_DIR(d) || d === "docs", config.ignore), { exceptions }));
|
|
278
|
-
}
|
|
279
|
-
findings = applySeverity(findings, config);
|
|
280
|
-
const nulls = flags.null ? flags.null.split(",").map((p) => loadNull(p.trim(), io.cwd)) : [];
|
|
281
|
-
const typical = nulls.length ? typicality(fp, nulls.flatMap((m, i) => m.runs.map((r) => nulls.length > 1 ? { ...r, id: `${i + 1}/${r.id}` } : r))) : null;
|
|
282
|
-
let estate = null;
|
|
283
|
-
if (flags.estate) {
|
|
284
|
-
const register = loadEstate(resolve(io.cwd, flags.estate));
|
|
285
|
-
const self = register.sites.find((s) => sameUrl(s.url, snap.url));
|
|
286
|
-
estate = compareToEstate(fp, register, { exclude: self?.id, siblingAt: SIBLING_AT });
|
|
287
|
-
}
|
|
288
|
-
const directionFile = resolve(repo ?? io.cwd, flags.direction ?? "art-direction.json");
|
|
289
|
-
let direction = null;
|
|
290
|
-
let directionReport = null;
|
|
291
|
-
if (existsSync(directionFile)) {
|
|
292
|
-
direction = JSON.parse(readFileSync(directionFile, "utf8"));
|
|
293
|
-
directionReport = validateDirection(direction, { fingerprint: fp, pathExists: (p) => existsSync(resolve(dirname(directionFile), p)) });
|
|
294
|
-
} else if (flags.direction) {
|
|
295
|
-
throw new Error(`no such file: ${flags.direction}`);
|
|
296
|
-
}
|
|
297
|
-
const copyTells = new Set(CATALOGUE.filter((t) => t.surface === "copy").map((t) => t.id));
|
|
298
|
-
const report = characterReport({ site: snap.url, findings, typicality: typical, estate, direction: directionReport }, copyTells);
|
|
299
|
-
if (command === "retrofit") {
|
|
300
|
-
const plan = retrofitPlan(report, direction);
|
|
301
|
-
if (flags.out) {
|
|
302
|
-
writeFileSync(resolve(io.cwd, flags.out), plan);
|
|
303
|
-
io.out(`craft retrofit: ${report.actions.length} step${report.actions.length === 1 ? "" : "s"} for ${snap.url} in ${flags.out}`);
|
|
304
|
-
} else io.out(plan);
|
|
305
|
-
return 0;
|
|
306
|
-
}
|
|
307
|
-
io.out(flags.json ? toJson(report) : formatCharacter(report));
|
|
308
|
-
if (flags.out) writeFileSync(resolve(io.cwd, flags.out), `${toJson(report)}
|
|
309
|
-
`);
|
|
310
|
-
return flags.strict && report.verdict !== "decided" ? 1 : 0;
|
|
311
|
-
} catch (error) {
|
|
312
|
-
io.err(`craft: ${error.message}`);
|
|
313
|
-
return 2;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
156
|
export {
|
|
317
|
-
|
|
318
|
-
|
|
157
|
+
TELL_HEAVY,
|
|
158
|
+
characterReport
|
|
319
159
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/null/generate.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { mkdtempSync, rmSync } from "fs";
|
|
4
|
+
import { tmpdir } from "os";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
function generate(prompt, model) {
|
|
7
|
+
const bin = process.env.CRAFT_CLAUDE ?? "claude";
|
|
8
|
+
const args = ["-p", "--tools", "", "--no-session-persistence", "--disable-slash-commands", "--setting-sources", "", "--strict-mcp-config", ...model ? ["--model", model] : [], prompt];
|
|
9
|
+
const cwd = mkdtempSync(join(tmpdir(), "craft-null-"));
|
|
10
|
+
return new Promise((done, fail) => {
|
|
11
|
+
const child = spawn(bin, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
12
|
+
let out = "";
|
|
13
|
+
let err = "";
|
|
14
|
+
child.stdout.on("data", (d) => out += d.toString());
|
|
15
|
+
child.stderr.on("data", (d) => err += d.toString());
|
|
16
|
+
child.on("error", (e) => fail(new Error(`${bin}: ${e.message}. Install Claude Code, or set CRAFT_CLAUDE.`)));
|
|
17
|
+
child.on("close", (code) => {
|
|
18
|
+
rmSync(cwd, { recursive: true, force: true });
|
|
19
|
+
if (code === 0) done(out);
|
|
20
|
+
else fail(new Error(`${bin} exited ${code}: ${(err || out).trim().slice(0, 200)}`));
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
generate
|
|
27
|
+
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
snapshotUrl,
|
|
3
3
|
snapshotUrls
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YK2FFL6Z.js";
|
|
5
5
|
import {
|
|
6
6
|
describeTypicality,
|
|
7
7
|
loadNull
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LWVGIJ5A.js";
|
|
9
9
|
import {
|
|
10
10
|
SOURCE_FILE,
|
|
11
11
|
finish,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
prepareReport,
|
|
16
16
|
readPaths,
|
|
17
17
|
toJson
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-LXDNIJFG.js";
|
|
19
19
|
import {
|
|
20
20
|
readSnapshot
|
|
21
21
|
} from "./chunk-WUACK2HX.js";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
fingerprint,
|
|
26
26
|
scanSource,
|
|
27
27
|
typicality
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-B2MJLGF6.js";
|
|
29
29
|
|
|
30
30
|
// src/audit/cli.ts
|
|
31
31
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|