create-claude-cabinet 0.29.12 → 0.29.13

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.
@@ -120,6 +120,17 @@ function extractAndInstall(versionDir, tarballPath, results) {
120
120
  results.push(' ⚠ browser-driver-manager install failed — axe-core will be unavailable');
121
121
  }
122
122
 
123
+ // MDN Observatory needs postinstall data files (HSTS preload list, TLD list)
124
+ const obsDir = path.join(pkgDir, 'node_modules', '@mdn', 'mdn-http-observatory');
125
+ if (fs.existsSync(obsDir)) {
126
+ try {
127
+ execSync('node src/retrieve-hsts.js && node src/retrieve-tld-list.js', { cwd: obsDir, encoding: 'utf8', timeout: 60_000 });
128
+ results.push(' Initialized MDN Observatory data files');
129
+ } catch {
130
+ results.push(' ⚠ MDN Observatory postinstall failed — observatory may be unavailable');
131
+ }
132
+ }
133
+
123
134
  const binDir = path.join(versionDir, 'bin');
124
135
  fs.mkdirSync(binDir, { recursive: true });
125
136
  const binSrc = path.join(pkgDir, 'bin', 'cc-site-audit');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-claude-cabinet",
3
- "version": "0.29.12",
3
+ "version": "0.29.13",
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.10",
3
+ "version": "0.1.11",
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": {
@@ -14,31 +14,74 @@ export async function run(url, executor) {
14
14
 
15
15
  const GRADE_SCORES = { 'A+': 100, 'A': 95, 'A-': 90, 'B+': 85, 'B': 80, 'B-': 75, 'C+': 70, 'C': 65, 'C-': 60, 'D+': 55, 'D': 50, 'D-': 45, 'F': 20 };
16
16
 
17
+ const TEST_LABELS = {
18
+ 'content-security-policy': 'Content Security Policy',
19
+ 'cookies': 'Cookies',
20
+ 'cross-origin-resource-sharing': 'CORS',
21
+ 'redirection': 'HTTP Redirection',
22
+ 'referrer-policy': 'Referrer Policy',
23
+ 'strict-transport-security': 'Strict Transport Security',
24
+ 'subresource-integrity': 'Subresource Integrity',
25
+ 'x-content-type-options': 'X-Content-Type-Options',
26
+ 'x-frame-options': 'X-Frame-Options',
27
+ 'cross-origin-resource-policy': 'Cross-Origin Resource Policy',
28
+ };
29
+
17
30
  export function normalize(raw, durationMs) {
18
31
  if (raw.code !== 0 && !raw.stdout) {
19
- return { checkId, tool, status: 'error', score: null, grade: null, severity: null, findings: [], durationMs, reason: raw.stderr || 'observatory failed' };
32
+ return { checkId, tool, status: 'error', score: null, grade: null, severity: null, findings: [], durationMs, reason: raw.stderr?.slice(0, 200) || 'observatory failed' };
33
+ }
34
+
35
+ const text = (raw.stdout || '').trim();
36
+
37
+ // Try JSON parse first (mdn-http-observatory outputs JSON)
38
+ const jsonStart = text.indexOf('{');
39
+ if (jsonStart >= 0) {
40
+ try {
41
+ const data = JSON.parse(text.slice(jsonStart));
42
+ const scan = data.scan || {};
43
+ const tests = data.tests || {};
44
+ const grade = scan.grade || null;
45
+ const score = typeof scan.score === 'number' ? Math.min(100, scan.score) : (grade ? (GRADE_SCORES[grade] ?? null) : null);
46
+
47
+ const findings = [];
48
+ for (const [testId, test] of Object.entries(tests)) {
49
+ if (!test.pass) {
50
+ const label = TEST_LABELS[testId] || testId;
51
+ const severity = (test.scoreModifier != null && test.scoreModifier <= -20) ? 'serious' : 'moderate';
52
+ findings.push({
53
+ severity,
54
+ message: `${label}: ${test.result || 'failed'}`,
55
+ context: test.scoreModifier != null ? `Score impact: ${test.scoreModifier}` : undefined,
56
+ });
57
+ }
58
+ }
59
+
60
+ const isPass = grade && /^[A-B]/.test(grade);
61
+ const passed = Object.values(tests).filter(t => t.pass).length;
62
+ const total = Object.keys(tests).length;
63
+ const passSummary = isPass
64
+ ? `Observatory grade ${grade} (score: ${score}/100, ${passed}/${total} tests passed)`
65
+ : undefined;
66
+
67
+ return {
68
+ checkId, tool, status: isPass ? 'pass' : 'fail',
69
+ score, grade, severity: findings.length ? findings[0].severity : null,
70
+ findings, durationMs,
71
+ ...(passSummary && { passSummary }),
72
+ };
73
+ } catch { /* fall through to text parsing */ }
20
74
  }
21
75
 
22
- const text = raw.stdout || '';
76
+ // Fallback: text parsing
23
77
  const gradeMatch = text.match(/Grade:\s*([A-F][+-]?)/i);
24
78
  const scoreMatch = text.match(/Score:\s*(\d+)/i);
25
79
  const grade = gradeMatch ? gradeMatch[1] : null;
26
80
  const score = scoreMatch ? Math.min(100, Number(scoreMatch[1])) : (grade ? (GRADE_SCORES[grade] ?? null) : null);
27
-
28
- const findings = [];
29
- const failLines = text.split('\n').filter(l => /fail|warn|not implemented/i.test(l));
30
- for (const line of failLines.slice(0, 20)) {
31
- findings.push({ severity: 'moderate', message: line.trim() });
32
- }
33
-
34
81
  const isPass = grade && /^[A-B]/.test(grade);
35
- const passSummary = isPass
36
- ? `Observatory grade ${grade}${score != null ? ` (score: ${score}/100)` : ''}`
37
- : undefined;
38
82
 
39
83
  return {
40
84
  checkId, tool, status: isPass ? 'pass' : 'fail',
41
- score, grade, severity: findings.length ? 'moderate' : null, findings, durationMs,
42
- ...(passSummary && { passSummary }),
85
+ score, grade, severity: null, findings: [], durationMs,
43
86
  };
44
87
  }
@@ -1,10 +1 @@
1
- Grade: B+
2
- Score: 85
3
- Tests passed: 8/11
4
- content-security-policy: PASS
5
- strict-transport-security: PASS
6
- x-content-type-options: PASS
7
- x-frame-options: FAIL - not implemented
8
- referrer-policy: PASS
9
- subresource-integrity: FAIL - not implemented
10
- cookies: WARN - SameSite not set
1
+ {"scan":{"grade":"B+","score":85,"testsFailed":2,"testsPassed":8,"testsQuantity":10},"tests":{"content-security-policy":{"pass":true,"result":"csp-implemented","scoreModifier":0},"strict-transport-security":{"pass":true,"result":"hsts-implemented","scoreModifier":0},"x-content-type-options":{"pass":true,"result":"x-content-type-options-nosniff","scoreModifier":0},"x-frame-options":{"pass":false,"result":"x-frame-options-not-implemented","scoreModifier":-20},"referrer-policy":{"pass":true,"result":"referrer-policy-private","scoreModifier":0},"subresource-integrity":{"pass":false,"result":"sri-not-implemented","scoreModifier":-5},"cookies":{"pass":true,"result":"cookies-secure","scoreModifier":0}}}
@@ -19,7 +19,7 @@ this project needs something different from the default.
19
19
 
20
20
  **`orient/phases/context.md`** — Point at the briefing files generated in
21
21
  the previous phase. At minimum:
22
- - Read `_briefing.md` for project identity and configuration
22
+ - Read `.claude/cabinet/_briefing.md` for project identity and configuration
23
23
  - Read `system-status.md` for current state
24
24
  - Read `CLAUDE.md` if it has project-specific instructions beyond what
25
25
  Claude Code auto-loads