@domandigital/craft 0.14.0 → 0.15.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 +18 -0
- package/CHARACTER.md +213 -28
- package/README.md +19 -1
- package/art-direction.schema.json +428 -25
- package/dist/{audit-6WHR7ZTV.js → audit-MTUC24XU.js} +2 -2
- package/dist/audit.cjs +5 -0
- package/dist/audit.js +5 -0
- package/dist/{chunk-7T5WR74C.js → chunk-2IQ5OXFR.js} +4 -4
- package/dist/{chunk-GQVEYTBY.js → chunk-LFNSB6W7.js} +13 -3
- package/dist/{chunk-3OTKMKJQ.js → chunk-MUVFECFC.js} +19 -15
- package/dist/chunk-OQXTQQAU.js +27 -0
- package/dist/{chunk-Q7ILL2HV.js → chunk-OUQ25LY2.js} +1 -1
- package/dist/{chunk-PBK6SYH5.js → chunk-QU6Q2QZZ.js} +46 -11
- package/dist/{chunk-XVBR2UPH.js → chunk-RXXRXV3V.js} +7 -24
- package/dist/chunk-YDURAOYR.js +711 -0
- package/dist/{cli-3CGUKXY4.js → cli-22TSWAX6.js} +6 -4
- package/dist/{cli-WSHPSWVU.js → cli-5ACKICBO.js} +6 -5
- package/dist/{cli-O3CDSONO.js → cli-7OZCYOQY.js} +86 -20
- package/dist/{cli-WG3UJNUF.js → cli-FCKHYAVZ.js} +36 -13
- package/dist/{cli-Z2IQG3LC.js → cli-FLG3ZCAF.js} +4 -3
- package/dist/cli.js +2 -2
- package/dist/generate.js +6 -0
- package/dist/index.cjs +850 -115
- package/dist/index.d.cts +455 -56
- package/dist/index.d.ts +455 -56
- package/dist/index.js +804 -115
- package/package.json +1 -1
- package/dist/chunk-OOZOEWGD.js +0 -295
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
protectedFacts,
|
|
17
17
|
scanSource,
|
|
18
18
|
visibleText
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-LFNSB6W7.js";
|
|
20
20
|
|
|
21
21
|
// src/character/config.ts
|
|
22
22
|
var DEFAULT_IGNORE = [".claude", ".agents", ".cursor"];
|
|
@@ -377,12 +377,13 @@ Usage
|
|
|
377
377
|
craft snapshot <url> [--out <file>] [--width <px>] [--height <px>]
|
|
378
378
|
craft audit <url | snapshot.json> [--repo <dir>] [--null <null.json>] [--out <file>] [--json] [--strict] [ci options]
|
|
379
379
|
craft audit --pages <sitemap.xml | urls.txt> [--viewport 390,768,1440] [the same]
|
|
380
|
-
craft direction init [--snapshot <file>] [--client <name>] [--out <file>]
|
|
381
|
-
craft direction validate [--snapshot <file>] [--direction <file>] [--json]
|
|
380
|
+
craft direction init [--snapshot <file | url>] [--client <name>] [--brief "<text>"] [--out <file>]
|
|
381
|
+
craft direction validate [--snapshot <file | url>] [--page home] [--estate <estate.json>] [--direction <file>] [--strict] [--json]
|
|
382
|
+
craft direction research [--brief "<who, where, what>"] [--visual]
|
|
382
383
|
craft direction propose [--snapshot <file>] [--estate <estate.json | dir>] [--out <file>]
|
|
383
384
|
craft null build --brief "<text>" --out <dir> [--runs 20] [--parallel 4] [--model <name>]
|
|
384
|
-
craft estate add <url | snapshot.json> --id <id> [--client <name>] [--estate <file>]
|
|
385
|
-
craft estate compare [<url | snapshot.json | id>] [--null <dir>] [--json] [--strict]
|
|
385
|
+
craft estate add [<url | snapshot.json>] --id <id> [--client <name>] [--direction <art-direction.json>] [--estate <file>]
|
|
386
|
+
craft estate compare [<url | snapshot.json | id>] [--null <dir>] [--directions] [--json] [--strict]
|
|
386
387
|
craft report <url | snapshot.json> [--repo <dir>] [--null <dir>] [--estate <file>] [--direction <file>] [--json] [--strict]
|
|
387
388
|
craft retrofit <url | snapshot.json> [the same] [--out RETROFIT.md]
|
|
388
389
|
|
|
@@ -461,10 +462,11 @@ var VALUE_FLAGS = {
|
|
|
461
462
|
"--pages": "pages",
|
|
462
463
|
"--viewport": "viewport",
|
|
463
464
|
"--component": "component",
|
|
464
|
-
"--competitor": "competitor"
|
|
465
|
+
"--competitor": "competitor",
|
|
466
|
+
"--page": "page"
|
|
465
467
|
};
|
|
466
468
|
function parseFlags(args) {
|
|
467
|
-
const flags = { positional: [], json: false, strict: false, staged: false, gate: false, updateBaseline: false };
|
|
469
|
+
const flags = { positional: [], json: false, strict: false, staged: false, gate: false, updateBaseline: false, directions: false, visual: false };
|
|
468
470
|
for (let i = 0; i < args.length; i += 1) {
|
|
469
471
|
const a = args[i];
|
|
470
472
|
if (a === "--json") flags.json = true;
|
|
@@ -472,6 +474,8 @@ function parseFlags(args) {
|
|
|
472
474
|
else if (a === "--staged") flags.staged = true;
|
|
473
475
|
else if (a === "--gate") flags.gate = true;
|
|
474
476
|
else if (a === "--update-baseline") flags.updateBaseline = true;
|
|
477
|
+
else if (a === "--directions") flags.directions = true;
|
|
478
|
+
else if (a === "--visual") flags.visual = true;
|
|
475
479
|
else if (a in VALUE_FLAGS) {
|
|
476
480
|
const value = args[i + 1];
|
|
477
481
|
i += 1;
|
|
@@ -576,7 +580,7 @@ async function pageSnapshot(target, cwd) {
|
|
|
576
580
|
return readSnapshot(JSON.parse(readFileSync(local, "utf8")), target);
|
|
577
581
|
}
|
|
578
582
|
if (!/^https?:\/\//.test(target)) throw new Error(`${target} is neither a URL nor a snapshot file`);
|
|
579
|
-
const { snapshotUrl } = await import("./audit-
|
|
583
|
+
const { snapshotUrl } = await import("./audit-MTUC24XU.js");
|
|
580
584
|
return snapshotUrl(target);
|
|
581
585
|
}
|
|
582
586
|
async function briefFor(flags, cwd) {
|
|
@@ -587,7 +591,7 @@ async function briefFor(flags, cwd) {
|
|
|
587
591
|
if (typeof brief === "string" && brief.trim()) return brief;
|
|
588
592
|
} else if (flags.direction) throw new Error(`no such file: ${flags.direction}`);
|
|
589
593
|
if (flags.null) {
|
|
590
|
-
const { loadNull } = await import("./cli-
|
|
594
|
+
const { loadNull } = await import("./cli-FLG3ZCAF.js");
|
|
591
595
|
return loadNull(flags.null, cwd).brief;
|
|
592
596
|
}
|
|
593
597
|
return void 0;
|
|
@@ -613,25 +617,25 @@ async function compareWithCompetitor(flags, io) {
|
|
|
613
617
|
function run(argv, io) {
|
|
614
618
|
const [command, ...rest] = argv;
|
|
615
619
|
if (command === "direction") {
|
|
616
|
-
return import("./cli-
|
|
620
|
+
return import("./cli-7OZCYOQY.js").then((m) => m.runDirection(rest, io));
|
|
617
621
|
}
|
|
618
622
|
if (command === "report" || command === "retrofit") {
|
|
619
|
-
return import("./cli-
|
|
623
|
+
return import("./cli-FCKHYAVZ.js").then((m) => m.runReport(command, rest, io));
|
|
620
624
|
}
|
|
621
625
|
if (command === "estate") {
|
|
622
|
-
return import("./cli-
|
|
626
|
+
return import("./cli-22TSWAX6.js").then((m) => m.runEstate(rest, io));
|
|
623
627
|
}
|
|
624
628
|
if (command === "null") {
|
|
625
|
-
return import("./cli-
|
|
629
|
+
return import("./cli-FLG3ZCAF.js").then((m) => m.runNull(rest, io));
|
|
626
630
|
}
|
|
627
631
|
if (command === "tells" && rest[0] === "harvest") {
|
|
628
|
-
return import("./cli-
|
|
632
|
+
return import("./cli-FLG3ZCAF.js").then((m) => m.runHarvest(rest, io)).catch((error) => {
|
|
629
633
|
io.err(`craft: ${error.message}`);
|
|
630
634
|
return 2;
|
|
631
635
|
});
|
|
632
636
|
}
|
|
633
637
|
if (command === "snapshot" || command === "audit") {
|
|
634
|
-
return import("./cli-
|
|
638
|
+
return import("./cli-5ACKICBO.js").then((m) => m.runAudit(command, rest, io));
|
|
635
639
|
}
|
|
636
640
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
637
641
|
io.out(HELP);
|
|
@@ -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,15 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
compareDirections,
|
|
3
|
+
compareToDeclared,
|
|
4
|
+
formatDirectionMatches,
|
|
5
|
+
summariseDirection,
|
|
6
|
+
validateDirection
|
|
7
|
+
} from "./chunk-YDURAOYR.js";
|
|
1
8
|
import {
|
|
2
9
|
loadNull
|
|
3
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-RXXRXV3V.js";
|
|
4
11
|
import {
|
|
5
12
|
parseFlags,
|
|
6
13
|
toJson
|
|
7
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-MUVFECFC.js";
|
|
8
15
|
import {
|
|
9
16
|
readSnapshot
|
|
10
17
|
} from "./chunk-WUACK2HX.js";
|
|
11
18
|
import {
|
|
12
19
|
COMPONENT_ROLES,
|
|
20
|
+
SHARED_PART,
|
|
13
21
|
auditSnapshot,
|
|
14
22
|
closestComponents,
|
|
15
23
|
estateComponentPairs,
|
|
@@ -18,7 +26,7 @@ import {
|
|
|
18
26
|
groundName,
|
|
19
27
|
hueName,
|
|
20
28
|
shapeName
|
|
21
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-LFNSB6W7.js";
|
|
22
30
|
|
|
23
31
|
// src/estate/cli.ts
|
|
24
32
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -37,7 +45,7 @@ function addSite(register, site) {
|
|
|
37
45
|
}
|
|
38
46
|
function sharedParts(a, b, d = fingerprintDistance(a, b)) {
|
|
39
47
|
const out = [];
|
|
40
|
-
const close = (k) => d.parts[k] <=
|
|
48
|
+
const close = (k) => d.parts[k] <= SHARED_PART;
|
|
41
49
|
if (close("accent")) out.push(a.accent || b.accent ? `${hueName(a.accent)} accent` : "no accent");
|
|
42
50
|
if (close("type")) {
|
|
43
51
|
if (a.display.family === b.display.family) out.push(`${a.display.family} headline`);
|
|
@@ -52,14 +60,14 @@ function sharedParts(a, b, d = fingerprintDistance(a, b)) {
|
|
|
52
60
|
}
|
|
53
61
|
function compareToEstate(target, register, options = {}) {
|
|
54
62
|
const at = options.siblingAt ?? SIBLING_AT;
|
|
55
|
-
return register.sites.filter((s) => s.id !== options.exclude).map((s) => {
|
|
63
|
+
return register.sites.filter((s) => s.id !== options.exclude && s.fingerprint).map((s) => {
|
|
56
64
|
const d = fingerprintDistance(target, s.fingerprint);
|
|
57
65
|
return { id: s.id, distance: d.total, sibling: d.total < at, shared: sharedParts(target, s.fingerprint, d) };
|
|
58
66
|
}).sort((a, b) => a.distance - b.distance);
|
|
59
67
|
}
|
|
60
68
|
function estatePairs(register, siblingAt = SIBLING_AT) {
|
|
61
69
|
const out = [];
|
|
62
|
-
const s = register.sites;
|
|
70
|
+
const s = register.sites.filter((site) => site.fingerprint);
|
|
63
71
|
for (let i = 0; i < s.length; i += 1) {
|
|
64
72
|
for (let j = i + 1; j < s.length; j += 1) {
|
|
65
73
|
const d = fingerprintDistance(s[i].fingerprint, s[j].fingerprint);
|
|
@@ -81,7 +89,7 @@ function siblingLine(briefs) {
|
|
|
81
89
|
}
|
|
82
90
|
|
|
83
91
|
// src/estate/cli.ts
|
|
84
|
-
var USAGE = "usage: craft estate add <url | snapshot.json> --id <id> [--client <name>] | craft estate compare [<url | snapshot.json | id>] [--null <dir>] [--component <role>] [--json] [--strict]";
|
|
92
|
+
var USAGE = "usage: craft estate add [<url | snapshot.json>] --id <id> [--client <name>] [--direction <art-direction.json>] | craft estate compare [<url | snapshot.json | id>] [--null <dir>] [--component <role>] [--directions] [--json] [--strict]";
|
|
85
93
|
function loadEstate(path) {
|
|
86
94
|
if (!existsSync(path)) return emptyEstate();
|
|
87
95
|
const data = JSON.parse(readFileSync(path, "utf8"));
|
|
@@ -95,7 +103,7 @@ async function snapshotOf(target, cwd) {
|
|
|
95
103
|
return snap;
|
|
96
104
|
}
|
|
97
105
|
if (!/^https?:\/\//.test(target)) throw new Error(`${target} is neither a URL, a snapshot file, nor a site in the register`);
|
|
98
|
-
const { snapshotUrl } = await import("./audit-
|
|
106
|
+
const { snapshotUrl } = await import("./audit-MTUC24XU.js");
|
|
99
107
|
return snapshotUrl(target);
|
|
100
108
|
}
|
|
101
109
|
var line = (distance, sibling, shared) => `${distance.toFixed(2)}${sibling ? " SIBLING" : ""}${shared.length ? ` shares ${shared.join(", ")}` : ""}`;
|
|
@@ -115,7 +123,7 @@ function componentRole(name) {
|
|
|
115
123
|
if (!COMPONENT_ROLES.includes(name)) throw new Error(`--component takes one of ${COMPONENT_ROLES.join(", ")}`);
|
|
116
124
|
return name;
|
|
117
125
|
}
|
|
118
|
-
var withRole = (register, role) => register.sites.filter((s) => (s.fingerprint
|
|
126
|
+
var withRole = (register, role) => register.sites.filter((s) => (s.fingerprint?.sections ?? []).some((x) => x.role === role)).length;
|
|
119
127
|
function formatComponentPairs(role, pairs, measured, sites) {
|
|
120
128
|
const lines = [`craft estate: ${role} components, ${measured} of ${sites} sites have one, closest pairs first (no sibling line yet: ranked, not judged)`];
|
|
121
129
|
for (const p of pairs) lines.push(` ${`${p.a} + ${p.b}`.padEnd(34)} ${p.distance.toFixed(2)}${p.shared.length ? ` shares ${p.shared.join(", ")}` : ""}`);
|
|
@@ -134,6 +142,19 @@ async function runEstate(args, io) {
|
|
|
134
142
|
const file = resolve(io.cwd, flags.estate ?? "estate.json");
|
|
135
143
|
const register = loadEstate(file);
|
|
136
144
|
const at = flags.null ? siblingLine(flags.null.split(",").map((p) => loadNull(p.trim(), io.cwd).runs.map((r) => r.fingerprint))) : SIBLING_AT;
|
|
145
|
+
const directionFile = flags.direction ? JSON.parse(readFileSync(resolve(io.cwd, flags.direction), "utf8")) : void 0;
|
|
146
|
+
if (directionFile && !validateDirection(directionFile).valid) throw new Error("the direction has errors: run craft direction validate before registering it");
|
|
147
|
+
const declared = () => summariseDirection(directionFile);
|
|
148
|
+
if (sub === "add" && !target && flags.id && flags.direction) {
|
|
149
|
+
const existing = register.sites.find((s) => s.id === flags.id);
|
|
150
|
+
const direction = declared();
|
|
151
|
+
const next = addSite(register, { ...existing ?? { id: flags.id, client: flags.client ?? directionFile.client, url: "", tells: [], addedAt: (/* @__PURE__ */ new Date()).toISOString() }, direction });
|
|
152
|
+
writeFileSync(file, `${JSON.stringify(next, null, 2)}
|
|
153
|
+
`);
|
|
154
|
+
io.out(`craft estate: ${flags.id} direction recorded.`);
|
|
155
|
+
io.out(formatDirectionMatches(flags.id, compareToDeclared(direction, next.sites, flags.id)));
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
137
158
|
if (sub === "add") {
|
|
138
159
|
if (!target || !flags.id) throw new Error(USAGE);
|
|
139
160
|
const snap = await snapshotOf(target, io.cwd);
|
|
@@ -144,7 +165,8 @@ async function runEstate(args, io) {
|
|
|
144
165
|
url: snap.url,
|
|
145
166
|
fingerprint: fp,
|
|
146
167
|
tells: [...new Set(auditSnapshot(snap).findings.map((f) => f.tell))].sort(),
|
|
147
|
-
addedAt: snap.capturedAt
|
|
168
|
+
addedAt: snap.capturedAt,
|
|
169
|
+
...flags.direction ? { direction: declared() } : register.sites.find((s) => s.id === flags.id)?.direction ? { direction: register.sites.find((s) => s.id === flags.id).direction } : {}
|
|
148
170
|
});
|
|
149
171
|
writeFileSync(file, `${JSON.stringify(next, null, 2)}
|
|
150
172
|
`);
|
|
@@ -152,6 +174,17 @@ async function runEstate(args, io) {
|
|
|
152
174
|
io.out(formatMatches(flags.id, compareToEstate(fp, next, { exclude: flags.id, siblingAt: at }).slice(0, 3), at));
|
|
153
175
|
return 0;
|
|
154
176
|
}
|
|
177
|
+
if (sub === "compare" && flags.directions) {
|
|
178
|
+
const withDirection = register.sites.filter((s) => s.direction);
|
|
179
|
+
const pairs = withDirection.flatMap((a, i) => withDirection.slice(i + 1).map((b) => ({ a: a.id, b: b.id, ...compareDirections(a.direction, b.direction) })));
|
|
180
|
+
pairs.sort((x, y) => y.shared.length - x.shared.length);
|
|
181
|
+
if (flags.json) io.out(toJson({ sites: withDirection.length, pairs }));
|
|
182
|
+
else {
|
|
183
|
+
io.out(`craft estate: ${withDirection.length} of ${register.sites.length} sites have a declared direction, ${pairs.filter((p) => p.flagged).length} pair(s) worth a look (ranked, not judged)`);
|
|
184
|
+
for (const p of pairs) io.out(` ${`${p.a} + ${p.b}`.padEnd(34)} ${p.flagged ? "LOOK " : " "}${p.shared.length ? `shares ${p.shared.join(", ")}` : "nothing shared"}`);
|
|
185
|
+
}
|
|
186
|
+
return 0;
|
|
187
|
+
}
|
|
155
188
|
if (sub === "compare" && flags.component) {
|
|
156
189
|
const role = componentRole(flags.component);
|
|
157
190
|
if (!target) {
|
|
@@ -161,9 +194,10 @@ async function runEstate(args, io) {
|
|
|
161
194
|
return 0;
|
|
162
195
|
}
|
|
163
196
|
const known = register.sites.find((s) => s.id === target);
|
|
197
|
+
if (known && !known.fingerprint) throw new Error(`${target} has a declared direction only; add a snapshot to compare rendered components`);
|
|
164
198
|
const fp = known ? known.fingerprint : fingerprint(await snapshotOf(target, io.cwd));
|
|
165
199
|
if (!(fp.sections ?? []).some((x) => x.role === role)) throw new Error(`${known?.id ?? target} has no ${role} section measured (roles need snapshot version 2)`);
|
|
166
|
-
const pairs = register.sites.filter((s) => s.id !== known?.id).flatMap((s) => {
|
|
200
|
+
const pairs = register.sites.filter((s) => s.id !== known?.id && s.fingerprint).flatMap((s) => {
|
|
167
201
|
const c = closestComponents(fp, s.fingerprint, role);
|
|
168
202
|
return c ? [{ a: known?.id ?? target, b: s.id, ...c }] : [];
|
|
169
203
|
}).sort((x, y) => x.distance - y.distance);
|
|
@@ -177,6 +211,7 @@ async function runEstate(args, io) {
|
|
|
177
211
|
return flags.strict && pairs.some((p) => p.sibling) ? 1 : 0;
|
|
178
212
|
}
|
|
179
213
|
const known = register.sites.find((s) => s.id === target);
|
|
214
|
+
if (known && !known.fingerprint) throw new Error(`${target} has a declared direction only; add a snapshot to compare rendered sites`);
|
|
180
215
|
const fp = known ? known.fingerprint : fingerprint(await snapshotOf(target, io.cwd));
|
|
181
216
|
const matches = compareToEstate(fp, register, { exclude: known?.id, siblingAt: at });
|
|
182
217
|
io.out(flags.json ? toJson({ siblingAt: at, matches }) : formatMatches(known?.id ?? target, matches, at));
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generate
|
|
3
|
+
} from "./chunk-OQXTQQAU.js";
|
|
1
4
|
import {
|
|
2
5
|
parseFlags,
|
|
3
6
|
toJson
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MUVFECFC.js";
|
|
5
8
|
import {
|
|
6
9
|
readSnapshot
|
|
7
10
|
} from "./chunk-WUACK2HX.js";
|
|
@@ -17,34 +20,14 @@ import {
|
|
|
17
20
|
hueSwatch,
|
|
18
21
|
nullPrompt,
|
|
19
22
|
scanSource
|
|
20
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-LFNSB6W7.js";
|
|
21
24
|
|
|
22
25
|
// src/null/cli.ts
|
|
23
|
-
import {
|
|
24
|
-
import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
|
|
25
|
-
import { tmpdir } from "os";
|
|
26
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
|
|
26
27
|
import { join, relative, resolve } from "path";
|
|
27
28
|
import { pathToFileURL } from "url";
|
|
28
29
|
var readJson = (path) => JSON.parse(readFileSync(path, "utf8"));
|
|
29
30
|
var pad = (n) => String(n).padStart(2, "0");
|
|
30
|
-
function generate(prompt, model) {
|
|
31
|
-
const bin = process.env.CRAFT_CLAUDE ?? "claude";
|
|
32
|
-
const args = ["-p", "--tools", "", "--no-session-persistence", "--disable-slash-commands", "--setting-sources", "", "--strict-mcp-config", ...model ? ["--model", model] : [], prompt];
|
|
33
|
-
const cwd = mkdtempSync(join(tmpdir(), "craft-null-"));
|
|
34
|
-
return new Promise((done, fail) => {
|
|
35
|
-
const child = spawn(bin, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
36
|
-
let out = "";
|
|
37
|
-
let err = "";
|
|
38
|
-
child.stdout.on("data", (d) => out += d.toString());
|
|
39
|
-
child.stderr.on("data", (d) => err += d.toString());
|
|
40
|
-
child.on("error", (e) => fail(new Error(`${bin}: ${e.message}. Install Claude Code, or set CRAFT_CLAUDE.`)));
|
|
41
|
-
child.on("close", (code) => {
|
|
42
|
-
rmSync(cwd, { recursive: true, force: true });
|
|
43
|
-
if (code === 0) done(out);
|
|
44
|
-
else fail(new Error(`${bin} exited ${code}: ${(err || out).trim().slice(0, 200)}`));
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
31
|
async function pool(items, size, work) {
|
|
49
32
|
const queue = [...items];
|
|
50
33
|
await Promise.all(Array.from({ length: Math.max(1, Math.min(size, queue.length)) }, async () => {
|
|
@@ -115,7 +98,7 @@ async function build(flags, io) {
|
|
|
115
98
|
const toShoot = ids.filter((id) => existsSync(join(pages, `${id}.html`)) && !existsSync(join(shots, `${id}.json`)));
|
|
116
99
|
if (toShoot.length) {
|
|
117
100
|
io.out(`craft null: snapshotting ${toShoot.length} page${toShoot.length === 1 ? "" : "s"}`);
|
|
118
|
-
const { snapshotUrls } = await import("./audit-
|
|
101
|
+
const { snapshotUrls } = await import("./audit-MTUC24XU.js");
|
|
119
102
|
const results = await snapshotUrls(toShoot.map((id) => pathToFileURL(join(pages, `${id}.html`)).href));
|
|
120
103
|
results.forEach((r, i) => {
|
|
121
104
|
const id = toShoot[i];
|