howone 0.2.0 → 0.2.2

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 (34) hide show
  1. package/bin/index.mjs +1 -1
  2. package/package.json +1 -1
  3. package/templates/vite/.howone/skills/howone/01-architect/01-app-generation.md +19 -16
  4. package/templates/vite/.howone/skills/howone/03-ai-capabilities/01-ai-capability-architecture.md +14 -24
  5. package/templates/vite/.howone/skills/howone/03-ai-capabilities/02-workflow-contract-rules.md +0 -107
  6. package/templates/vite/.howone/skills/howone/03-ai-capabilities/03-service-capability-catalog.md +26 -101
  7. package/templates/vite/.howone/skills/howone/03-ai-capabilities/04-workflow-operations.md +23 -17
  8. package/templates/vite/.howone/skills/howone/03-ai-capabilities/05-ai-feature-playbooks.md +1 -66
  9. package/templates/vite/.howone/skills/howone/SKILL.md +5 -4
  10. package/templates/vite/.howone/skills/design-taste-frontend/SKILL.md +0 -1206
  11. package/templates/vite/.howone/skills/web-clone/LICENSE +0 -21
  12. package/templates/vite/.howone/skills/web-clone/README.md +0 -179
  13. package/templates/vite/.howone/skills/web-clone/SKILL.md +0 -243
  14. package/templates/vite/.howone/skills/web-clone/references/assessment.md +0 -77
  15. package/templates/vite/.howone/skills/web-clone/references/complex-playbooks.md +0 -46
  16. package/templates/vite/.howone/skills/web-clone/references/deliverables.md +0 -144
  17. package/templates/vite/.howone/skills/web-clone/references/design-dna.md +0 -125
  18. package/templates/vite/.howone/skills/web-clone/references/effect-extraction.md +0 -73
  19. package/templates/vite/.howone/skills/web-clone/references/marbles-case.md +0 -31
  20. package/templates/vite/.howone/skills/web-clone/references/reverse-engineering.md +0 -34
  21. package/templates/vite/.howone/skills/web-clone/references/static-mirror.md +0 -72
  22. package/templates/vite/.howone/skills/web-clone/scripts/asset-harvest.mjs +0 -101
  23. package/templates/vite/.howone/skills/web-clone/scripts/audit-clone.mjs +0 -151
  24. package/templates/vite/.howone/skills/web-clone/scripts/compare-recon.mjs +0 -265
  25. package/templates/vite/.howone/skills/web-clone/scripts/dna-scaffold.mjs +0 -214
  26. package/templates/vite/.howone/skills/web-clone/scripts/init-clone.mjs +0 -136
  27. package/templates/vite/.howone/skills/web-clone/scripts/interaction-probe.mjs +0 -314
  28. package/templates/vite/.howone/skills/web-clone/scripts/lib/playwright-loader.mjs +0 -39
  29. package/templates/vite/.howone/skills/web-clone/scripts/mirror-site.mjs +0 -121
  30. package/templates/vite/.howone/skills/web-clone/scripts/network-capture.mjs +0 -127
  31. package/templates/vite/.howone/skills/web-clone/scripts/recon-site.mjs +0 -235
  32. package/templates/vite/.howone/skills/web-clone/scripts/route-crawl.mjs +0 -228
  33. package/templates/vite/.howone/skills/web-clone/scripts/sourcemap-hunt.mjs +0 -112
  34. package/templates/vite/.howone/skills/web-clone/scripts/visual-diff.mjs +0 -161
@@ -1,161 +0,0 @@
1
- #!/usr/bin/env node
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import { loadPlaywright, launchChromium } from "./lib/playwright-loader.mjs";
5
-
6
- function usage() {
7
- console.log(`Usage:
8
- node scripts/visual-diff.mjs --original <png> --clone <png> --out visual-diff.json [--diff visual-diff.png] [--threshold 0.08]
9
-
10
- Compares screenshots in a real browser canvas and outputs numeric visual-diff metrics.
11
- `);
12
- }
13
-
14
- function parseArgs(argv) {
15
- const out = { original: "", clone: "", out: "visual-diff.json", diff: "", threshold: 0.08 };
16
- for (let i = 0; i < argv.length; i += 1) {
17
- const arg = argv[i];
18
- if (arg === "--help" || arg === "-h") out.help = true;
19
- else if (arg === "--original") out.original = argv[++i] || "";
20
- else if (arg === "--clone") out.clone = argv[++i] || "";
21
- else if (arg === "--out") out.out = argv[++i] || "visual-diff.json";
22
- else if (arg === "--diff") out.diff = argv[++i] || "";
23
- else if (arg === "--threshold") out.threshold = Number(argv[++i] || "0.08");
24
- else throw new Error(`Unexpected argument: ${arg}`);
25
- }
26
- return out;
27
- }
28
-
29
- function imageDataUrl(file) {
30
- const ext = path.extname(file).slice(1).toLowerCase() || "png";
31
- const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
32
- return `data:${mime};base64,${fs.readFileSync(file).toString("base64")}`;
33
- }
34
-
35
- function scoreFromDiff(diffRatio, meanAbsDiff) {
36
- if (diffRatio <= 0.01 && meanAbsDiff <= 0.01) return 5;
37
- if (diffRatio <= 0.04 && meanAbsDiff <= 0.025) return 4.5;
38
- if (diffRatio <= 0.08 && meanAbsDiff <= 0.05) return 4;
39
- if (diffRatio <= 0.16 && meanAbsDiff <= 0.08) return 3;
40
- if (diffRatio <= 0.3 && meanAbsDiff <= 0.14) return 2;
41
- return 1;
42
- }
43
-
44
- async function compareInBrowser(page, original, clone, threshold) {
45
- return page.evaluate(async ({ original, clone, threshold }) => {
46
- const loadImage = (src) => new Promise((resolve, reject) => {
47
- const image = new Image();
48
- image.onload = () => resolve(image);
49
- image.onerror = () => reject(new Error(`Failed to load image: ${src.slice(0, 80)}`));
50
- image.src = src;
51
- });
52
-
53
- const [left, right] = await Promise.all([loadImage(original), loadImage(clone)]);
54
- const width = Math.max(left.naturalWidth, right.naturalWidth);
55
- const height = Math.max(left.naturalHeight, right.naturalHeight);
56
-
57
- const canvasA = document.createElement("canvas");
58
- const canvasB = document.createElement("canvas");
59
- const canvasD = document.createElement("canvas");
60
- for (const canvas of [canvasA, canvasB, canvasD]) {
61
- canvas.width = width;
62
- canvas.height = height;
63
- }
64
-
65
- const ctxA = canvasA.getContext("2d");
66
- const ctxB = canvasB.getContext("2d");
67
- const ctxD = canvasD.getContext("2d");
68
- ctxA.fillStyle = "white";
69
- ctxB.fillStyle = "white";
70
- ctxA.fillRect(0, 0, width, height);
71
- ctxB.fillRect(0, 0, width, height);
72
- ctxA.drawImage(left, 0, 0);
73
- ctxB.drawImage(right, 0, 0);
74
-
75
- const a = ctxA.getImageData(0, 0, width, height);
76
- const b = ctxB.getImageData(0, 0, width, height);
77
- const d = ctxD.createImageData(width, height);
78
- let changed = 0;
79
- let sumAbs = 0;
80
- let sumSq = 0;
81
-
82
- for (let i = 0; i < a.data.length; i += 4) {
83
- const dr = Math.abs(a.data[i] - b.data[i]);
84
- const dg = Math.abs(a.data[i + 1] - b.data[i + 1]);
85
- const db = Math.abs(a.data[i + 2] - b.data[i + 2]);
86
- const da = Math.abs(a.data[i + 3] - b.data[i + 3]);
87
- const delta = (dr + dg + db + da) / 1020;
88
- sumAbs += delta;
89
- sumSq += delta * delta;
90
- if (delta > threshold) changed += 1;
91
-
92
- if (delta > threshold) {
93
- d.data[i] = 255;
94
- d.data[i + 1] = Math.max(0, 80 - delta * 80);
95
- d.data[i + 2] = Math.max(0, 80 - delta * 80);
96
- d.data[i + 3] = 255;
97
- } else {
98
- d.data[i] = Math.round(a.data[i] * 0.25 + 245 * 0.75);
99
- d.data[i + 1] = Math.round(a.data[i + 1] * 0.25 + 245 * 0.75);
100
- d.data[i + 2] = Math.round(a.data[i + 2] * 0.25 + 245 * 0.75);
101
- d.data[i + 3] = 255;
102
- }
103
- }
104
-
105
- ctxD.putImageData(d, 0, 0);
106
- const pixels = width * height;
107
- const diffPixelRatio = changed / pixels;
108
- const meanAbsDiff = sumAbs / pixels;
109
- const rmse = Math.sqrt(sumSq / pixels);
110
-
111
- return {
112
- original: { width: left.naturalWidth, height: left.naturalHeight },
113
- clone: { width: right.naturalWidth, height: right.naturalHeight },
114
- comparedCanvas: { width, height },
115
- threshold,
116
- changedPixels: changed,
117
- totalPixels: pixels,
118
- diffPixelRatio,
119
- meanAbsDiff,
120
- rmse,
121
- diffPngDataUrl: canvasD.toDataURL("image/png"),
122
- };
123
- }, { original, clone, threshold });
124
- }
125
-
126
- try {
127
- const args = parseArgs(process.argv.slice(2));
128
- if (args.help || !args.original || !args.clone) {
129
- usage();
130
- process.exit(args.help ? 0 : 1);
131
- }
132
-
133
- const { chromium } = loadPlaywright();
134
- const browser = await launchChromium(chromium);
135
- const page = await browser.newPage();
136
- const result = await compareInBrowser(page, imageDataUrl(args.original), imageDataUrl(args.clone), args.threshold);
137
- await browser.close();
138
-
139
- const diffDataUrl = result.diffPngDataUrl;
140
- delete result.diffPngDataUrl;
141
- result.visualScore = scoreFromDiff(result.diffPixelRatio, result.meanAbsDiff);
142
- result.files = {
143
- original: path.resolve(args.original),
144
- clone: path.resolve(args.clone),
145
- diff: args.diff ? path.resolve(args.diff) : "",
146
- };
147
-
148
- fs.mkdirSync(path.dirname(path.resolve(args.out)), { recursive: true });
149
- fs.writeFileSync(args.out, `${JSON.stringify(result, null, 2)}\n`);
150
-
151
- if (args.diff) {
152
- const base64 = diffDataUrl.replace(/^data:image\/png;base64,/, "");
153
- fs.mkdirSync(path.dirname(path.resolve(args.diff)), { recursive: true });
154
- fs.writeFileSync(args.diff, Buffer.from(base64, "base64"));
155
- }
156
-
157
- console.log(path.resolve(args.out));
158
- } catch (error) {
159
- console.error(`visual-diff failed: ${error.message}`);
160
- process.exit(1);
161
- }