create-claude-cabinet 0.29.8 → 0.29.9

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.
@@ -93,6 +93,20 @@ function extractAndInstall(versionDir, tarballPath, results) {
93
93
 
94
94
  // Pa11y needs Puppeteer's Chrome; axe-core needs chromedriver.
95
95
  // Both were skipped by --ignore-scripts. Install them now.
96
+ // Clear corrupt Puppeteer cache first (folder exists but binary missing).
97
+ try {
98
+ const cacheBase = path.join(os.homedir(), '.cache', 'puppeteer', 'chrome');
99
+ if (fs.existsSync(cacheBase)) {
100
+ for (const d of fs.readdirSync(cacheBase)) {
101
+ const chromeBin = path.join(cacheBase, d, 'chrome-mac-arm64', 'Google Chrome for Testing.app', 'Contents', 'MacOS', 'Google Chrome for Testing');
102
+ const chromeLinux = path.join(cacheBase, d, 'chrome-linux64', 'chrome');
103
+ if (!fs.existsSync(chromeBin) && !fs.existsSync(chromeLinux)) {
104
+ fs.rmSync(path.join(cacheBase, d), { recursive: true, force: true });
105
+ results.push(` Cleared corrupt Puppeteer cache: ${d}`);
106
+ }
107
+ }
108
+ }
109
+ } catch { /* tolerate */ }
96
110
  try {
97
111
  execSync('npx puppeteer browsers install chrome', { cwd: pkgDir, encoding: 'utf8', timeout: 120_000 });
98
112
  results.push(' Installed Puppeteer Chrome for Pa11y');
@@ -100,10 +114,10 @@ function extractAndInstall(versionDir, tarballPath, results) {
100
114
  results.push(' ⚠ Puppeteer Chrome install failed — Pa11y will be unavailable');
101
115
  }
102
116
  try {
103
- execSync('node node_modules/chromedriver/install.js', { cwd: pkgDir, encoding: 'utf8', timeout: 120_000 });
104
- results.push(' Installed chromedriver for axe-core');
117
+ execSync('npx browser-driver-manager install chrome', { cwd: pkgDir, encoding: 'utf8', timeout: 120_000 });
118
+ results.push(' Installed matching Chrome + chromedriver for axe-core');
105
119
  } catch {
106
- results.push(' ⚠ chromedriver install failed — axe-core will be unavailable');
120
+ results.push(' ⚠ browser-driver-manager install failed — axe-core will be unavailable');
107
121
  }
108
122
 
109
123
  const binDir = path.join(versionDir, 'bin');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-cabinet",
3
- "version": "0.29.8",
3
+ "version": "0.29.9",
4
4
  "description": "Claude Cabinet — opinionated process scaffolding for Claude Code projects",
5
5
  "bin": {
6
6
  "create-claude-cabinet": "bin/create-claude-cabinet.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-cabinet/site-audit",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Comprehensive deployed-site quality audit engine for Claude Cabinet. Runs checks across performance, accessibility, security, SEO, content, DNS, and privacy against a deployed URL; single-site and comparison modes; standalone HTML report.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,7 @@ export async function detect(executor) {
8
8
  }
9
9
 
10
10
  export async function run(url, executor) {
11
- return executor.spawn('axe', [url, '--exit'], { timeoutMs: 60_000 });
11
+ return executor.spawn('axe', [url, '--stdout'], { timeoutMs: 60_000 });
12
12
  }
13
13
 
14
14
  const IMPACT_TO_SEVERITY = { critical: 'critical', serious: 'serious', moderate: 'moderate', minor: 'info' };
@@ -2,6 +2,9 @@
2
2
  // aggregated scores. Heavier than single-page Lighthouse — use for
3
3
  // multi-page audit coverage.
4
4
 
5
+ import { readFileSync, existsSync, readdirSync, rmSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+
5
8
  export const checkId = 'unlighthouse';
6
9
  export const tool = 'Unlighthouse (full-site crawl)';
7
10
  export const whyItMatters = "Runs Lighthouse on every page of your site, not just the homepage — catches performance and accessibility problems hiding on inner pages.";
@@ -13,14 +16,44 @@ export async function detect(executor) {
13
16
  }
14
17
 
15
18
  export async function run(url, executor) {
16
- return executor.spawn('unlighthouse', [
17
- '--site', url, '--ci', '--reporter', 'json',
18
- ], { timeoutMs: 300_000 });
19
+ const outDir = join(process.cwd(), '.unlighthouse-tmp');
20
+ try { rmSync(outDir, { recursive: true, force: true }); } catch { /* ok */ }
21
+
22
+ const r = await executor.spawn('unlighthouse-ci', [
23
+ '--site', url,
24
+ '--reporter', 'jsonExpanded',
25
+ '--output-path', outDir,
26
+ '--samples', '5',
27
+ ], { timeoutMs: 600_000 });
28
+
29
+ // Read the JSON report from the output directory
30
+ try {
31
+ const reportDir = join(outDir, 'ci-result');
32
+ if (existsSync(reportDir)) {
33
+ const files = readdirSync(reportDir).filter(f => f.endsWith('.json'));
34
+ if (files.length) {
35
+ const json = readFileSync(join(reportDir, files[0]), 'utf8');
36
+ return { code: r.code, stdout: json, stderr: r.stderr, timedOut: r.timedOut };
37
+ }
38
+ }
39
+ // Fallback: try .unlighthouse directory
40
+ const altDir = join(outDir);
41
+ const altFiles = existsSync(altDir) ? readdirSync(altDir).filter(f => f.endsWith('.json')) : [];
42
+ if (altFiles.length) {
43
+ const json = readFileSync(join(altDir, altFiles[0]), 'utf8');
44
+ return { code: r.code, stdout: json, stderr: r.stderr, timedOut: r.timedOut };
45
+ }
46
+ } catch { /* fall through */ }
47
+
48
+ return r;
19
49
  }
20
50
 
21
51
  export function normalize(raw, durationMs) {
52
+ if (raw.timedOut) {
53
+ return { checkId, tool, status: 'error', score: null, grade: null, severity: null, findings: [], durationMs, reason: 'timed out (full-site crawl may need more time)' };
54
+ }
22
55
  if (raw.code !== 0 && !raw.stdout) {
23
- return { checkId, tool, status: 'error', score: null, grade: null, severity: null, findings: [], durationMs, reason: raw.stderr || 'unlighthouse failed' };
56
+ return { checkId, tool, status: 'error', score: null, grade: null, severity: null, findings: [], durationMs, reason: raw.stderr?.slice(0, 200) || 'unlighthouse failed' };
24
57
  }
25
58
 
26
59
  let data;
@@ -40,7 +40,7 @@ h1{font-size:1.75rem;margin-bottom:.25rem}
40
40
  .sev-info{color:#888}
41
41
  .finding-url{font-size:.8rem;color:#06c;word-break:break-all}
42
42
  .pass-summary{color:#0a7;font-size:.9rem;font-style:italic;padding:.25rem 0}
43
- .compare-row{display:grid;grid-template-columns:1fr 100px 100px 80px;gap:.5rem;align-items:center;padding:.75rem 1.25rem;border-bottom:1px solid #f0f0f0;cursor:pointer;text-decoration:none;color:inherit}
43
+ .compare-row{display:grid;grid-template-columns:1fr 150px 150px 80px;gap:.5rem;align-items:center;padding:.75rem 1.25rem;border-bottom:1px solid #f0f0f0;cursor:pointer;text-decoration:none;color:inherit}
44
44
  .compare-row:hover{background:#f8f9fc}
45
45
  .compare-row:first-child{font-weight:700;background:#f8f9fc;cursor:default}
46
46
  .delta-pos{color:#0a7;font-weight:600}
@@ -260,7 +260,18 @@ function classifyFindings(a, b) {
260
260
  * @returns {string}
261
261
  */
262
262
  function hostnameLabel(url) {
263
- try { return new URL(url).hostname; } catch { return url; }
263
+ try {
264
+ const u = new URL(url);
265
+ const h = u.hostname;
266
+ const platformDomains = ['railway.app', 'herokuapp.com', 'vercel.app', 'netlify.app', 'fly.dev', 'render.com'];
267
+ const isPlat = platformDomains.some(d => h.endsWith(d));
268
+ if (isPlat && u.pathname && u.pathname !== '/') {
269
+ return u.pathname.split('/').filter(Boolean)[0] + ' (staging)';
270
+ }
271
+ if (isPlat) return 'staging';
272
+ if (h.length <= 25) return h;
273
+ return h.slice(0, 22) + '...';
274
+ } catch { return url; }
264
275
  }
265
276
 
266
277
  export function renderComparison(delta) {