howone 0.1.53 → 0.2.1

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 (33) hide show
  1. package/bin/index.mjs +362 -5
  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/web-clone/LICENSE +0 -21
  11. package/templates/vite/.howone/skills/web-clone/README.md +0 -179
  12. package/templates/vite/.howone/skills/web-clone/SKILL.md +0 -243
  13. package/templates/vite/.howone/skills/web-clone/references/assessment.md +0 -77
  14. package/templates/vite/.howone/skills/web-clone/references/complex-playbooks.md +0 -46
  15. package/templates/vite/.howone/skills/web-clone/references/deliverables.md +0 -144
  16. package/templates/vite/.howone/skills/web-clone/references/design-dna.md +0 -125
  17. package/templates/vite/.howone/skills/web-clone/references/effect-extraction.md +0 -73
  18. package/templates/vite/.howone/skills/web-clone/references/marbles-case.md +0 -31
  19. package/templates/vite/.howone/skills/web-clone/references/reverse-engineering.md +0 -34
  20. package/templates/vite/.howone/skills/web-clone/references/static-mirror.md +0 -72
  21. package/templates/vite/.howone/skills/web-clone/scripts/asset-harvest.mjs +0 -101
  22. package/templates/vite/.howone/skills/web-clone/scripts/audit-clone.mjs +0 -151
  23. package/templates/vite/.howone/skills/web-clone/scripts/compare-recon.mjs +0 -265
  24. package/templates/vite/.howone/skills/web-clone/scripts/dna-scaffold.mjs +0 -214
  25. package/templates/vite/.howone/skills/web-clone/scripts/init-clone.mjs +0 -136
  26. package/templates/vite/.howone/skills/web-clone/scripts/interaction-probe.mjs +0 -314
  27. package/templates/vite/.howone/skills/web-clone/scripts/lib/playwright-loader.mjs +0 -39
  28. package/templates/vite/.howone/skills/web-clone/scripts/mirror-site.mjs +0 -121
  29. package/templates/vite/.howone/skills/web-clone/scripts/network-capture.mjs +0 -127
  30. package/templates/vite/.howone/skills/web-clone/scripts/recon-site.mjs +0 -235
  31. package/templates/vite/.howone/skills/web-clone/scripts/route-crawl.mjs +0 -228
  32. package/templates/vite/.howone/skills/web-clone/scripts/sourcemap-hunt.mjs +0 -112
  33. 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
- }