mosaic-headless 1.29.2 → 1.30.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 (44) hide show
  1. package/README.md +17 -5
  2. package/SKILL.md +44 -5
  3. package/assets/templates/platforms/claude-ai.json +1 -1
  4. package/assets/templates/platforms/claude-code.json +1 -1
  5. package/assets/templates/platforms/codex-cli.json +1 -1
  6. package/assets/templates/platforms/copilot.json +1 -1
  7. package/assets/templates/platforms/gemini-cli.json +1 -1
  8. package/data/browser-verification/moksa.csv +3986 -0
  9. package/data/browser-verification/my-account.csv +148 -0
  10. package/data/node-properties.csv +2 -2
  11. package/package.json +10 -2
  12. package/references/responsive.md +1 -0
  13. package/scripts/check-release.mjs +39 -6
  14. package/scripts/sync-version.mjs +3 -2
  15. package/sites/_components.py +560 -0
  16. package/sites/_content.py +341 -0
  17. package/sites/_conversion.py +225 -0
  18. package/sites/_dialogs.py +579 -0
  19. package/sites/_dynamic.py +311 -0
  20. package/sites/_forms.py +487 -0
  21. package/sites/_sliders.py +437 -0
  22. package/sites/_tokens.py +277 -0
  23. package/tools/build_page.py +4 -2
  24. package/tools/build_site.py +4 -2
  25. package/tools/copy_styles.py +4 -2
  26. package/tools/extract_default_children.py +1 -1
  27. package/tools/extract_node_types.py +1 -1
  28. package/tools/mosaic_config.py +71 -0
  29. package/tools/probe.py +4 -2
  30. package/tools/probe_accordion.py +4 -2
  31. package/tools/sweep_components.py +4 -2
  32. package/tools/sweep_interactions.py +4 -2
  33. package/tools/sweep_node_properties.py +4 -2
  34. package/tools/sweep_node_types.py +4 -2
  35. package/tools/sweep_properties.py +4 -2
  36. package/tools/sweep_style_properties.py +4 -2
  37. package/tools/sweep_style_states.py +4 -2
  38. package/tools/theme_zip.py +6 -3
  39. package/tools/verify_browser.py +24 -10
  40. package/tools/verify_conversion.py +6 -2
  41. package/tools/verify_intro.py +4 -2
  42. package/tools/verify_rwd.py +4 -2
  43. package/data/browser-verification.csv +0 -4133
  44. package/sites/moksa.json +0 -63681
@@ -0,0 +1,148 @@
1
+ breakpoint,attrID,property,declared,computed,status
2
+ _,mk-acct-body,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
3
+ _,mk-acct-body,paddingLeft,40px,40px,ok
4
+ _,mk-acct-body,paddingRight,40px,40px,ok
5
+ _,mk-acct-body,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
6
+ _,mk-acct-body-in,width,100%,viewport- or parent-relative,not-comparable
7
+ _,mk-acct-body-in,maxWidth,1240px,1240px,ok
8
+ _,mk-acct-body-in,marginLeft,auto,auto resolves to a used value (60px),not-comparable
9
+ _,mk-acct-body-in,marginRight,auto,auto resolves to a used value (60px),not-comparable
10
+ _,mk-acct-head,display,grid,grid,ok
11
+ _,mk-acct-head,gridCols,84px 1fr,84px 1156px,ok
12
+ _,mk-acct-head,columnGap,0px,0px,ok
13
+ _,mk-acct-head,alignItems,start,start,ok
14
+ _,mk-acct-head,paddingTop,22px,22px,ok
15
+ _,mk-acct-head,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
16
+ _,mk-acct-mast,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
17
+ _,mk-acct-mast,paddingLeft,40px,40px,ok
18
+ _,mk-acct-mast,paddingRight,40px,40px,ok
19
+ _,mk-acct-mast,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
20
+ _,mk-acct-mast-in,width,100%,viewport- or parent-relative,not-comparable
21
+ _,mk-acct-mast-in,maxWidth,1240px,1240px,ok
22
+ _,mk-acct-mast-in,marginLeft,auto,auto resolves to a used value (60px),not-comparable
23
+ _,mk-acct-mast-in,marginRight,auto,auto resolves to a used value (60px),not-comparable
24
+ _,mk-acct-pad,paddingTop,70px,70px,ok
25
+ _,mk-acct-pad,paddingBottom,48px,48px,ok
26
+ _,mk-acct-r-0,display,grid,grid,ok
27
+ _,mk-acct-r-0,gridCols,150px 1fr,150px 986px,ok
28
+ _,mk-acct-r-0,columnGap,20px,20px,ok
29
+ _,mk-acct-r-0,alignItems,baseline,baseline,ok
30
+ _,mk-acct-r-0,paddingTop,13px,13px,ok
31
+ _,mk-acct-r-0,paddingBottom,13px,13px,ok
32
+ _,mk-acct-r-0,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
33
+ _,mk-acct-r-1,display,grid,grid,ok
34
+ _,mk-acct-r-1,gridCols,150px 1fr,150px 986px,ok
35
+ _,mk-acct-r-1,columnGap,20px,20px,ok
36
+ _,mk-acct-r-1,alignItems,baseline,baseline,ok
37
+ _,mk-acct-r-1,paddingTop,13px,13px,ok
38
+ _,mk-acct-r-1,paddingBottom,13px,13px,ok
39
+ _,mk-acct-r-1,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
40
+ _,mk-acct-r-2,display,grid,grid,ok
41
+ _,mk-acct-r-2,gridCols,150px 1fr,150px 986px,ok
42
+ _,mk-acct-r-2,columnGap,20px,20px,ok
43
+ _,mk-acct-r-2,alignItems,baseline,baseline,ok
44
+ _,mk-acct-r-2,paddingTop,13px,13px,ok
45
+ _,mk-acct-r-2,paddingBottom,13px,13px,ok
46
+ _,mk-acct-r-2,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
47
+ _,mk-acct-rows,marginTop,30px,30px,ok
48
+ _,mk-acct-rows,display,grid,grid,ok
49
+ _,mk-acct-rows,rowGap,0px,0px,ok
50
+ _,mk-acct-sheet,paddingBottom,120px,120px,ok
51
+ _t,mk-acct-body,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
52
+ _t,mk-acct-body,paddingLeft,28px,28px,ok
53
+ _t,mk-acct-body,paddingRight,28px,28px,ok
54
+ _t,mk-acct-body,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
55
+ _t,mk-acct-body-in,width,100%,viewport- or parent-relative,not-comparable
56
+ _t,mk-acct-body-in,maxWidth,1240px,1240px,ok
57
+ _t,mk-acct-body-in,marginLeft,auto,auto resolves to a used value (0px),not-comparable
58
+ _t,mk-acct-body-in,marginRight,auto,auto resolves to a used value (0px),not-comparable
59
+ _t,mk-acct-head,display,grid,grid,ok
60
+ _t,mk-acct-head,gridCols,84px 1fr,84px 760px,ok
61
+ _t,mk-acct-head,columnGap,0px,0px,ok
62
+ _t,mk-acct-head,alignItems,start,start,ok
63
+ _t,mk-acct-head,paddingTop,22px,22px,ok
64
+ _t,mk-acct-head,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
65
+ _t,mk-acct-mast,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
66
+ _t,mk-acct-mast,paddingLeft,28px,28px,ok
67
+ _t,mk-acct-mast,paddingRight,28px,28px,ok
68
+ _t,mk-acct-mast,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
69
+ _t,mk-acct-mast-in,width,100%,viewport- or parent-relative,not-comparable
70
+ _t,mk-acct-mast-in,maxWidth,1240px,1240px,ok
71
+ _t,mk-acct-mast-in,marginLeft,auto,auto resolves to a used value (0px),not-comparable
72
+ _t,mk-acct-mast-in,marginRight,auto,auto resolves to a used value (0px),not-comparable
73
+ _t,mk-acct-pad,paddingTop,50px,50px,ok
74
+ _t,mk-acct-pad,paddingBottom,48px,48px,ok
75
+ _t,mk-acct-r-0,display,grid,grid,ok
76
+ _t,mk-acct-r-0,gridCols,150px 1fr,150px 590px,ok
77
+ _t,mk-acct-r-0,columnGap,20px,20px,ok
78
+ _t,mk-acct-r-0,alignItems,baseline,baseline,ok
79
+ _t,mk-acct-r-0,paddingTop,13px,13px,ok
80
+ _t,mk-acct-r-0,paddingBottom,13px,13px,ok
81
+ _t,mk-acct-r-0,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
82
+ _t,mk-acct-r-1,display,grid,grid,ok
83
+ _t,mk-acct-r-1,gridCols,150px 1fr,150px 590px,ok
84
+ _t,mk-acct-r-1,columnGap,20px,20px,ok
85
+ _t,mk-acct-r-1,alignItems,baseline,baseline,ok
86
+ _t,mk-acct-r-1,paddingTop,13px,13px,ok
87
+ _t,mk-acct-r-1,paddingBottom,13px,13px,ok
88
+ _t,mk-acct-r-1,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
89
+ _t,mk-acct-r-2,display,grid,grid,ok
90
+ _t,mk-acct-r-2,gridCols,150px 1fr,150px 590px,ok
91
+ _t,mk-acct-r-2,columnGap,20px,20px,ok
92
+ _t,mk-acct-r-2,alignItems,baseline,baseline,ok
93
+ _t,mk-acct-r-2,paddingTop,13px,13px,ok
94
+ _t,mk-acct-r-2,paddingBottom,13px,13px,ok
95
+ _t,mk-acct-r-2,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
96
+ _t,mk-acct-rows,marginTop,30px,30px,ok
97
+ _t,mk-acct-rows,display,grid,grid,ok
98
+ _t,mk-acct-rows,rowGap,0px,0px,ok
99
+ _t,mk-acct-sheet,paddingBottom,120px,120px,ok
100
+ _m,mk-acct-body,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
101
+ _m,mk-acct-body,paddingLeft,18px,18px,ok
102
+ _m,mk-acct-body,paddingRight,18px,18px,ok
103
+ _m,mk-acct-body,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
104
+ _m,mk-acct-body-in,width,100%,viewport- or parent-relative,not-comparable
105
+ _m,mk-acct-body-in,maxWidth,1240px,1240px,ok
106
+ _m,mk-acct-body-in,marginLeft,auto,auto resolves to a used value (0px),not-comparable
107
+ _m,mk-acct-body-in,marginRight,auto,auto resolves to a used value (0px),not-comparable
108
+ _m,mk-acct-head,display,grid,grid,ok
109
+ _m,mk-acct-head,gridCols,48px 1fr,48px 306px,ok
110
+ _m,mk-acct-head,columnGap,0px,0px,ok
111
+ _m,mk-acct-head,alignItems,start,start,ok
112
+ _m,mk-acct-head,paddingTop,22px,22px,ok
113
+ _m,mk-acct-head,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
114
+ _m,mk-acct-mast,backgroundColor,"rgb(250,250,247)","rgb(250, 250, 247)",ok
115
+ _m,mk-acct-mast,paddingLeft,18px,18px,ok
116
+ _m,mk-acct-mast,paddingRight,18px,18px,ok
117
+ _m,mk-acct-mast,fontFamily,"'Noto Sans TC','PingFang TC','Hiragino Sans TC','Microsoft JhengHei',sans-serif","""Noto Sans TC"", ""PingFang TC"", ""Hiragino Sans TC"", ""Microsoft JhengHei"", sans-serif",ok
118
+ _m,mk-acct-mast-in,width,100%,viewport- or parent-relative,not-comparable
119
+ _m,mk-acct-mast-in,maxWidth,1240px,1240px,ok
120
+ _m,mk-acct-mast-in,marginLeft,auto,auto resolves to a used value (0px),not-comparable
121
+ _m,mk-acct-mast-in,marginRight,auto,auto resolves to a used value (0px),not-comparable
122
+ _m,mk-acct-pad,paddingTop,24px,24px,ok
123
+ _m,mk-acct-pad,paddingBottom,34px,34px,ok
124
+ _m,mk-acct-r-0,display,grid,grid,ok
125
+ _m,mk-acct-r-0,gridCols,84px 1fr,84px 210px,ok
126
+ _m,mk-acct-r-0,columnGap,12px,12px,ok
127
+ _m,mk-acct-r-0,alignItems,baseline,baseline,ok
128
+ _m,mk-acct-r-0,paddingTop,13px,13px,ok
129
+ _m,mk-acct-r-0,paddingBottom,13px,13px,ok
130
+ _m,mk-acct-r-0,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
131
+ _m,mk-acct-r-1,display,grid,grid,ok
132
+ _m,mk-acct-r-1,gridCols,84px 1fr,84px 210px,ok
133
+ _m,mk-acct-r-1,columnGap,12px,12px,ok
134
+ _m,mk-acct-r-1,alignItems,baseline,baseline,ok
135
+ _m,mk-acct-r-1,paddingTop,13px,13px,ok
136
+ _m,mk-acct-r-1,paddingBottom,13px,13px,ok
137
+ _m,mk-acct-r-1,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
138
+ _m,mk-acct-r-2,display,grid,grid,ok
139
+ _m,mk-acct-r-2,gridCols,84px 1fr,84px 210px,ok
140
+ _m,mk-acct-r-2,columnGap,12px,12px,ok
141
+ _m,mk-acct-r-2,alignItems,baseline,baseline,ok
142
+ _m,mk-acct-r-2,paddingTop,13px,13px,ok
143
+ _m,mk-acct-r-2,paddingBottom,13px,13px,ok
144
+ _m,mk-acct-r-2,customDeclarations:border-top,"1px solid rgba(22,24,28,.16)",no computed value,not-comparable
145
+ _m,mk-acct-rows,marginTop,30px,30px,ok
146
+ _m,mk-acct-rows,display,grid,grid,ok
147
+ _m,mk-acct-rows,rowGap,0px,0px,ok
148
+ _m,mk-acct-sheet,paddingBottom,72px,72px,ok
@@ -60,7 +60,7 @@ WPMenuElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,me
60
60
  NavbarElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,buttonBreakpoint,ValidateAllowUndefined|ValidatorAcceptedValues|ValidatorBreakpoint|Validators,none,true,,data-animation|data-toggle-breakpoint-id,Mosaic/NodeTypes/Navbar/NavbarElementMResourceData.php
61
61
  NavbarElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,animation,ValidateAllowUndefined|ValidatorAcceptedValues|Validators,none|fade|fadeDown|fadeUp|scaleDown|flipDown,true,,data-animation|data-toggle-breakpoint-id,Mosaic/NodeTypes/Navbar/NavbarElementMResourceData.php
62
62
  NavbarElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,disableScroll,ValidateAllowUndefined|ValidatorAcceptedValues|Validators,0|1,true,,data-animation|data-toggle-breakpoint-id,Mosaic/NodeTypes/Navbar/NavbarElementMResourceData.php
63
- NodeMResourceDataAbstract,MResourceDataAbstract,free,createDataSimple,currentStatus,ValidatorAcceptedValues,,,,,Mosaic/NodeTypes/NodeAbstract/NodeMResourceDataAbstract.php
63
+ NodeMResourceDataAbstract,MResourceDataAbstract,free,createDataSimple,currentStatus,ValidatorAcceptedValues,PUBLISH|DELETE,,,,Mosaic/NodeTypes/NodeAbstract/NodeMResourceDataAbstract.php
64
64
  NodeMResourceDataAbstract,MResourceDataAbstract,free,createDataGroup,override,,,true,,,Mosaic/NodeTypes/NodeAbstract/NodeMResourceDataAbstract.php
65
65
  OpenStreetMapElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,latitude,ValidateAllowUndefined|ValidatorDynamicCodeObject|ValidatorFloat|ValidatorFloatAsString|Validators,,true,,,Mosaic/NodeTypes/OpenStreetMap/OpenStreetMapElementMResourceData.php
66
66
  OpenStreetMapElementMResourceData,ElementMResourceDataAbstract,free,createDataSimple,longitude,ValidateAllowUndefined|ValidatorDynamicCodeObject|ValidatorFloat|ValidatorFloatAsString|Validators,,true,,,Mosaic/NodeTypes/OpenStreetMap/OpenStreetMapElementMResourceData.php
@@ -134,7 +134,7 @@ SliderElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,aut
134
134
  SliderElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,isCarousel,ValidateAllowUndefined|ValidatorAcceptedValues|Validators,0|1,true,,data-animation-type|data-duration|data-easing|data-autoplay|data-autoplay-delay|data-autoplay-limit|data-carousel|data-default-slide-index|data-slide-label|role|aria-roledescription,Mosaic/NodeTypes/Pro/Slider/SliderElementMResourceData.php
135
135
  SliderElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,defaultSlideIndex,ValidateAllowUndefined|ValidatorInteger|ValidatorIntegerAsString|Validators,,true,,data-animation-type|data-duration|data-easing|data-autoplay|data-autoplay-delay|data-autoplay-limit|data-carousel|data-default-slide-index|data-slide-label|role|aria-roledescription,Mosaic/NodeTypes/Pro/Slider/SliderElementMResourceData.php
136
136
  SliderElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,ariaLabel,ValidateAllowUndefined|ValidatorString|Validators,,true,,data-animation-type|data-duration|data-easing|data-autoplay|data-autoplay-delay|data-autoplay-limit|data-carousel|data-default-slide-index|data-slide-label|role|aria-roledescription,Mosaic/NodeTypes/Pro/Slider/SliderElementMResourceData.php
137
- SliderSlideElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,image,ValidateAllowUndefined|ValidatorAcceptedValues|ValidatorDynamicCodeObject|ValidatorImageAttachment|ValidatorURL|Validators,,true,,,Mosaic/NodeTypes/Pro/Slider/SliderSlides/SliderSlide/SliderSlideElementMResourceData.php
137
+ SliderSlideElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,image,ValidateAllowUndefined|ValidatorAcceptedValues|ValidatorDynamicCodeObject|ValidatorImageAttachment|ValidatorURL|Validators,MosaicDefaultURL,true,,,Mosaic/NodeTypes/Pro/Slider/SliderSlides/SliderSlide/SliderSlideElementMResourceData.php
138
138
  SliderSlideElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,positionX,ValidateAllowUndefined|ValidatorStyleBackground|ValidatorStyleBackgroundPosition|Validators,,true,,,Mosaic/NodeTypes/Pro/Slider/SliderSlides/SliderSlide/SliderSlideElementMResourceData.php
139
139
  SliderSlideElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,positionY,ValidateAllowUndefined|ValidatorStyleBackground|ValidatorStyleBackgroundPosition|Validators,,true,,,Mosaic/NodeTypes/Pro/Slider/SliderSlides/SliderSlide/SliderSlideElementMResourceData.php
140
140
  SliderSlideElementMResourceData,ElementMResourceDataAbstract,pro,createDataSimple,alt,ValidateAllowUndefined|ValidatorDynamicCodeObject|ValidatorString|Validators,,true,,,Mosaic/NodeTypes/Pro/Slider/SliderSlides/SliderSlide/SliderSlideElementMResourceData.php
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mosaic-headless",
3
- "version": "1.29.2",
3
+ "version": "1.30.1",
4
4
  "description": "AI-agent skill: build Mosaic Pro (Nextend) WordPress sites by writing the underlying data model directly - 23 custom tables, no visual editor, no DOM. Every node type, style property and node property swept against a live install and asserted on the delivered HTML and compiled CSS. Installs into Claude Code, Cursor, Codex CLI, Gemini CLI, Copilot, Continue, Windsurf and Claude.ai.",
5
5
  "keywords": [
6
6
  "wordpress",
@@ -33,9 +33,17 @@
33
33
  "scripts/",
34
34
  "assets/",
35
35
  "data/*.csv",
36
+ "data/browser-verification/*.csv",
36
37
  "references/",
37
38
  "sites/_moksa.py",
38
- "sites/moksa.json",
39
+ "sites/_components.py",
40
+ "sites/_content.py",
41
+ "sites/_conversion.py",
42
+ "sites/_dialogs.py",
43
+ "sites/_dynamic.py",
44
+ "sites/_forms.py",
45
+ "sites/_sliders.py",
46
+ "sites/_tokens.py",
39
47
  "tools/*.py",
40
48
  "tools/*.php",
41
49
  "SKILL.md",
@@ -136,6 +136,7 @@ mechanical check reads what the spec declared and what the site actually served,
136
136
  compares them key by key:
137
137
 
138
138
  ```bash
139
+ python sites/_moksa.py # moksa.json is generated, not shipped
139
140
  python tools/verify_rwd.py --config sweep.json --site sites/moksa.json --csv rwd.csv
140
141
  ```
141
142
 
@@ -26,16 +26,48 @@ import { fileURLToPath } from "node:url";
26
26
 
27
27
  const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
28
28
  const read = (p) => fs.readFileSync(path.join(ROOT, p), "utf8");
29
+ // A table split one file per page (data/browser-verification/) is still one table:
30
+ // its CSVs, in name order, each with its own header.
31
+ const tableFiles = (p) => fs.statSync(path.join(ROOT, p)).isDirectory()
32
+ ? fs.readdirSync(path.join(ROOT, p)).filter((f) => f.endsWith(".csv")).sort()
33
+ .map((f) => `${p}/${f}`)
34
+ : [p];
29
35
  const problems = [];
30
36
  const fail = (m) => problems.push(m);
31
37
 
38
+ // ---------- the plugin directory's limits -----------------------------------
39
+ //
40
+ // The Claude plugin directory reads the repository root, every tracked file of it,
41
+ // and holds a submission for a reviewer when any file that is not an image or font
42
+ // is over 256 KiB, or when there are more than 512 files. A 400 KB CSV and a 2 MB
43
+ // generated site file did exactly that. Checked here, so it fails on this machine
44
+ // rather than in a review queue a week later.
45
+ try {
46
+ const tracked = execSync("git ls-files -z", { cwd: ROOT }).toString().split("\0").filter(Boolean);
47
+ if (tracked.length > 512) fail(`${tracked.length} tracked files - the plugin directory allows 512`);
48
+ for (const f of tracked) {
49
+ if (/\.(png|jpe?g|gif|webp|svg|ico|woff2?|ttf|otf)$/i.test(f)) continue;
50
+ const full = path.join(ROOT, f);
51
+ if (!fs.existsSync(full)) continue;
52
+ const size = fs.statSync(full).size;
53
+ if (size > 256 * 1024)
54
+ fail(`${f} is ${(size / 1024).toFixed(0)} KiB - the plugin directory holds any non-image file over 256 KiB`);
55
+ }
56
+ } catch {
57
+ // not a git checkout (an unpacked tarball): nothing to measure against
58
+ }
59
+
32
60
  // ---------- versions agree --------------------------------------------------
33
61
 
34
62
  const pkg = JSON.parse(read("package.json"));
35
63
  const want = pkg.version;
36
64
 
37
65
  const skill = read("SKILL.md");
38
- const fmVersion = /^version:\s*"?([^"\n]+)"?\s*$/m.exec(skill.split("---")[1] || "");
66
+ // `version` sits under `metadata:`, hence the leading indent. The Agent Skills
67
+ // spec allows only allowed-tools / compatibility / description / license /
68
+ // metadata / name at the top level, and a stray `version` there does not get
69
+ // ignored - it fails the upload.
70
+ const fmVersion = /^\s+version:\s*"?([^"\n]+)"?\s*$/m.exec(skill.split("---")[1] || "");
39
71
  if (!fmVersion) fail("SKILL.md frontmatter has no version:");
40
72
  else if (fmVersion[1].trim() !== want)
41
73
  fail(`SKILL.md version ${fmVersion[1].trim()} != package.json ${want}`);
@@ -87,13 +119,14 @@ if (!fs.existsSync(path.join(ROOT, pkg.bin["mosaic-headless"])))
87
119
 
88
120
  // ---------- the numbers in the docs are counted, not trusted ----------------
89
121
 
90
- const rows = (p) => read(p).trim().split("\n").length - 1; // minus the header
122
+ const rows = (p) => tableFiles(p)
123
+ .reduce((n, f) => n + read(f).trim().split("\n").length - 1, 0); // minus each header
91
124
  const counts = {
92
125
  "data/node-verification.csv": 126,
93
126
  "data/style-verification.csv": 98,
94
127
  "data/node-property-verification.csv": 191,
95
128
  "data/rwd-verification.csv": 733,
96
- "data/browser-verification.csv": 4132,
129
+ "data/browser-verification": 4132,
97
130
  "data/style-state-verification.csv": 52,
98
131
  "data/interaction-verification.csv": 7,
99
132
  "data/data-class-hierarchy.csv": 125,
@@ -127,7 +160,7 @@ if (!style.includes("SKIPPED")) fail("style-verification.csv has no SKIPPED rows
127
160
 
128
161
  // The browser pass has the same obligation, under a different word: a declaration it
129
162
  // cannot soundly compare must say so rather than be counted as agreement.
130
- const browser = read("data/browser-verification.csv");
163
+ const browser = tableFiles("data/browser-verification").map(read).join("\n");
131
164
  if (!browser.includes("not-comparable"))
132
165
  fail("browser-verification.csv has no not-comparable rows - a computed-value check "
133
166
  + "that claims to compare everything is comparing things it cannot");
@@ -186,13 +219,13 @@ if (/,FAIL,/.test(zip)) fail("data/theme-zip-verification.csv carries a failed c
186
219
  // rwd and browser counts, and must not carry the previous ones.
187
220
  {
188
221
  const fmt = (n) => n.toLocaleString("en-US");
189
- const want = [rows("data/rwd-verification.csv"), rows("data/browser-verification.csv")];
222
+ const want = [rows("data/rwd-verification.csv"), rows("data/browser-verification")];
190
223
  for (const readme of ["README.md", "README.zh-TW.md", "README.ja.md", "README.ko.md"]) {
191
224
  const text = read(readme);
192
225
  for (const n of want)
193
226
  if (!text.includes(fmt(n)) && !text.includes(String(n)))
194
227
  fail(`${readme} does not quote ${fmt(n)} - it is quoting a stale count`);
195
- if (/0 findings|指摘 0 件|지적 0건|0 項發現/.test(text))
228
+ if (/\b0 findings\b|指摘 0 件|지적 0건|0 項發現/.test(text))
196
229
  fail(`${readme} still claims a design audit with 0 findings`);
197
230
  }
198
231
  }
@@ -38,12 +38,13 @@ const head = skill.slice(0, end);
38
38
  const rest = skill.slice(end);
39
39
  // "already correct" and "line missing" are different answers, and conflating them
40
40
  // made this exit 1 on a release where the version happened to match already.
41
- const VERSION_LINE = /^version:\s*"?[^"\n]*"?\s*$/m;
41
+ // indented: it lives under `metadata:` (see check-release.mjs for why)
42
+ const VERSION_LINE = /^(\s+)version:\s*"?[^"\n]*"?\s*$/m;
42
43
  if (!VERSION_LINE.test(head)) {
43
44
  console.error("SKILL.md frontmatter has no version: line to update");
44
45
  process.exit(1);
45
46
  }
46
- const next = head.replace(VERSION_LINE, `version: "${v}"`);
47
+ const next = head.replace(VERSION_LINE, (_m, indent) => `${indent}version: "${v}"`);
47
48
  if (next !== head) {
48
49
  fs.writeFileSync(skillPath, next + rest);
49
50
  touched.push("SKILL.md");