skills-ws 1.3.1 → 1.4.0

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 (116) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/REVIEW.md +516 -0
  3. package/SECURITY.md +1 -1
  4. package/app/cli/page.tsx +100 -0
  5. package/app/docs/page.tsx +117 -0
  6. package/app/faq/page.tsx +92 -0
  7. package/app/globals.css +25 -0
  8. package/app/layout.tsx +248 -0
  9. package/app/not-found.tsx +22 -0
  10. package/app/page.tsx +141 -0
  11. package/app/sitemap.ts +25 -0
  12. package/app/skills/[name]/page.tsx +192 -0
  13. package/components/AsciiBackground.tsx +207 -0
  14. package/components/FaqAccordion.tsx +45 -0
  15. package/components/InstallBox.tsx +43 -0
  16. package/components/NpmDownloads.tsx +43 -0
  17. package/components/SkillContent.tsx +103 -0
  18. package/components/SkillsGrid.tsx +132 -0
  19. package/lib/skills.ts +47 -0
  20. package/next-env.d.ts +5 -0
  21. package/next.config.mjs +7 -0
  22. package/npm-package.json +56 -0
  23. package/package.json +28 -17
  24. package/postcss.config.mjs +8 -0
  25. package/public/favicon.ico +0 -0
  26. package/public/favicon.svg +4 -0
  27. package/public/install.sh +111 -0
  28. package/public/llms-full.txt +1000 -0
  29. package/public/llms.txt +83 -0
  30. package/public/og.png +0 -0
  31. package/public/robots.txt +51 -0
  32. package/public/skills.json +2335 -0
  33. package/skills/ai-agent-building/SKILL.md +988 -0
  34. package/skills/aleph-cloud-self-deployment/SKILL.md +678 -0
  35. package/skills/api-design/SKILL.md +827 -145
  36. package/skills/aws-production-deploy/SKILL.md +951 -0
  37. package/skills/ci-cd-pipeline/SKILL.md +846 -0
  38. package/skills/content-strategy/SKILL.md +831 -57
  39. package/skills/copywriting/SKILL.md +859 -84
  40. package/skills/docker-production/SKILL.md +744 -0
  41. package/skills/email-sequence/SKILL.md +673 -55
  42. package/skills/monitoring-observability/SKILL.md +872 -0
  43. package/skills/nextjs-performance/SKILL.md +557 -0
  44. package/skills/page-cro/SKILL.md +730 -76
  45. package/skills/paid-ads/SKILL.md +632 -74
  46. package/skills/popup-cro/SKILL.md +892 -34
  47. package/skills/postgres-mastery/SKILL.md +765 -0
  48. package/skills/programmatic-seo/SKILL.md +888 -47
  49. package/skills/security-hardening/SKILL.md +754 -104
  50. package/skills/seo-geo/SKILL.md +549 -91
  51. package/skills/signup-flow-cro/SKILL.md +1357 -60
  52. package/skills/stripe-billing/SKILL.md +735 -0
  53. package/skills/ui-ux-pro-max/SKILL.md +1122 -50
  54. package/skills-data/ab-testing/SKILL.md +171 -0
  55. package/skills-data/accounting-finance/SKILL.md +139 -0
  56. package/skills-data/affiliate-marketing/SKILL.md +152 -0
  57. package/{skills → skills-data}/ai-agent-design/SKILL.md +2 -2
  58. package/skills-data/api-design/SKILL.md +226 -0
  59. package/skills-data/ascii-banner/SKILL.md +317 -0
  60. package/skills-data/bing-webmaster/SKILL.md +130 -0
  61. package/skills-data/blog-engine/SKILL.md +91 -0
  62. package/skills-data/brand-strategy/SKILL.md +205 -0
  63. package/skills-data/business-development/SKILL.md +140 -0
  64. package/skills-data/cicd-pipelines/SKILL.md +232 -0
  65. package/skills-data/cold-outreach/SKILL.md +169 -0
  66. package/skills-data/community-building/SKILL.md +144 -0
  67. package/skills-data/competitor-intelligence/SKILL.md +145 -0
  68. package/skills-data/content-strategy/SKILL.md +85 -0
  69. package/skills-data/copywriting/SKILL.md +88 -0
  70. package/skills-data/crm-builder/SKILL.md +86 -0
  71. package/skills-data/crm-operations/SKILL.md +148 -0
  72. package/skills-data/customer-acquisition/SKILL.md +133 -0
  73. package/skills-data/customer-feedback/SKILL.md +140 -0
  74. package/skills-data/data-analytics/SKILL.md +203 -0
  75. package/skills-data/data-management/SKILL.md +199 -0
  76. package/skills-data/database-design/SKILL.md +158 -0
  77. package/skills-data/email-sequence/SKILL.md +81 -0
  78. package/skills-data/eu-legal-compliance/SKILL.md +156 -0
  79. package/skills-data/git-workflow/SKILL.md +200 -0
  80. package/skills-data/google-analytics/SKILL.md +188 -0
  81. package/skills-data/growth-hacking/SKILL.md +75 -0
  82. package/skills-data/hiring-team-building/SKILL.md +172 -0
  83. package/skills-data/influencer-marketing/SKILL.md +181 -0
  84. package/skills-data/landing-page-builder/SKILL.md +93 -0
  85. package/skills-data/lead-scoring/SKILL.md +64 -0
  86. package/skills-data/local-seo/SKILL.md +70 -0
  87. package/skills-data/marketing-analytics/SKILL.md +106 -0
  88. package/skills-data/mvp-launcher/SKILL.md +145 -0
  89. package/skills-data/nextjs-stack/SKILL.md +231 -0
  90. package/skills-data/page-cro/SKILL.md +80 -0
  91. package/skills-data/paid-ads/SKILL.md +110 -0
  92. package/skills-data/popup-cro/SKILL.md +52 -0
  93. package/skills-data/pr-media-outreach/SKILL.md +140 -0
  94. package/skills-data/pricing-optimization/SKILL.md +316 -0
  95. package/skills-data/programmatic-seo/SKILL.md +65 -0
  96. package/skills-data/project-management/SKILL.md +161 -0
  97. package/skills-data/prompt-engineering/SKILL.md +189 -0
  98. package/skills-data/retention-analytics/SKILL.md +161 -0
  99. package/skills-data/revenue-operations/SKILL.md +162 -0
  100. package/skills-data/sales-funnel/SKILL.md +56 -0
  101. package/skills-data/search-console/SKILL.md +159 -0
  102. package/skills-data/security-hardening/SKILL.md +176 -0
  103. package/skills-data/seo-geo/SKILL.md +138 -0
  104. package/skills-data/signup-flow-cro/SKILL.md +64 -0
  105. package/skills-data/smart-contract-auditor/SKILL.md +64 -0
  106. package/skills-data/social-media-growth/SKILL.md +146 -0
  107. package/skills-data/social-media-kit/SKILL.md +56 -0
  108. package/skills-data/testing-strategy/SKILL.md +344 -0
  109. package/skills-data/ui-ux-pro-max/SKILL.md +72 -0
  110. package/skills-data/virustotal/SKILL.md +108 -0
  111. package/skills-data/web-performance/SKILL.md +219 -0
  112. package/skills-data/webinar-events/SKILL.md +148 -0
  113. package/skills-data/yandex-webmaster/SKILL.md +159 -0
  114. package/skills.json +2335 -0
  115. package/tailwind.config.ts +45 -0
  116. package/tsconfig.json +26 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,39 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased] — 2026-02-28
4
+
5
+ ### Added — Premium Skills Tier
6
+
7
+ **10 premium skills added:**
8
+ - `aws-production-deploy` — ECS, RDS, CloudFront, Route53, SSL, monitoring, CDK/Terraform
9
+ - `stripe-billing` — Subscriptions, usage-based billing, webhooks, customer portal, metering
10
+ - `security-hardening` — OWASP Top 10, CSP, rate limiting, auth, pentesting checklist
11
+ - `ai-agent-building` — CrewAI, LangGraph, tool use, memory systems, multi-agent orchestration
12
+ - `nextjs-performance` — Core Web Vitals, ISR/SSG, edge functions, bundle analysis
13
+ - `postgres-mastery` — Indexes, query optimization, partitioning, pgvector, migrations
14
+ - `docker-production` — Multi-stage builds, compose, secrets, health checks, security
15
+ - `api-design` — REST best practices, versioning, pagination, OpenAPI, rate limiting
16
+ - `monitoring-observability` — Prometheus, Grafana, Datadog, SLOs, OpenTelemetry
17
+ - `ci-cd-pipeline` — GitHub Actions, testing pyramid, deployment gates, feature flags
18
+
19
+ **UI changes:**
20
+ - Premium skills show 🔒 lock icon and amber "Premium" badge on skill cards
21
+ - Premium skill detail pages show 30-line preview with gradient fade, then paywall CTA
22
+ - CTA button: "Get all premium skills — $49/year" linking to `NEXT_PUBLIC_PREMIUM_URL` env var
23
+ - New "★ Premium" filter button in skill category bar
24
+ - Premium skill count shown in footer stats (amber colored)
25
+ - Free skills remain fully accessible — no changes
26
+
27
+ **Files changed:**
28
+ - `lib/skills.ts` — Added `premium?: boolean` to Skill interface
29
+ - `skills.json` + `public/skills.json` — 10 new skills with `"premium": true`
30
+ - `components/SkillsGrid.tsx` — Premium badge, lock icon, premium filter button
31
+ - `components/PremiumGate.tsx` — New component: preview + paywall CTA
32
+ - `app/skills/[name]/page.tsx` — Premium badge on detail page, PremiumGate for premium content
33
+ - `app/page.tsx` — Premium skills count in stats section
34
+
35
+ **Notes:**
36
+ - Total skills: 80 (70 free + 10 premium)
37
+ - Static export — paywall is client-side only (intentionally bypassable for v1)
38
+ - CLI package unchanged — premium skills are web-only
39
+ - Build passes: 86 static pages generated
package/REVIEW.md ADDED
@@ -0,0 +1,516 @@
1
+ # Repository Review & Code Audit — skills.ws
2
+
3
+ **Date:** 2026-02-26
4
+ **Repo:** san-npm/skills-ws
5
+ **Branch:** claude/repo-review-audit-DLdT5
6
+ **Stack:** Next.js 14.2 (static export), React 18, TypeScript, Tailwind CSS, Three.js
7
+
8
+ ---
9
+
10
+ ## Project Overview
11
+
12
+ A Next.js website for **skills.ws** — a catalog of 60 agent skills (SKILL.md files) for AI coding assistants (OpenClaw, Claude Code, Cursor, Codex, Gemini CLI). The site serves as a marketing homepage, documentation hub, and installation gateway via `npx skills-ws`.
13
+
14
+ ### Architecture
15
+
16
+ ```
17
+ app/
18
+ layout.tsx — Root layout: nav, GA4, 4× JSON-LD schemas
19
+ page.tsx — Homepage: ASCII banner, search/filter grid, stats
20
+ sitemap.ts — Dynamic XML sitemap
21
+ not-found.tsx — Custom 404
22
+ skills/[name]/ — Dynamic skill detail pages (SSG)
23
+ docs/ — Static docs page
24
+ cli/ — CLI reference page
25
+ faq/ — FAQ with accordion + FAQPage schema
26
+ components/
27
+ AsciiBackground — WebGL Three.js → ASCII art canvas (homepage only)
28
+ SkillsGrid — Client-side search/filter grid
29
+ SkillContent — Markdown renderer (react-markdown + remark-gfm)
30
+ InstallBox — Copy-to-clipboard install command
31
+ FaqAccordion — Expandable Q&A
32
+ NpmDownloads — Live npm download counter (client-side, cached)
33
+ lib/
34
+ skills.ts — Skill data access, types, category colors
35
+ skills.json — 60 skills with full markdown content (~357KB)
36
+ skills-data/ — 60 directories, each with SKILL.md
37
+ public/
38
+ skills.json — Served copy (should match root)
39
+ install.sh — Bash installer script
40
+ llms.txt — LLM-readable skill index
41
+ llms-full.txt — Full LLM content dump
42
+ robots.txt — Crawl directives + sitemap
43
+ og.png — OpenGraph image (1200×630)
44
+ favicon.ico — Favicon (32×32)
45
+ favicon.svg — SVG favicon
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Findings Summary
51
+
52
+ | Severity | Count |
53
+ |----------|-------|
54
+ | CRITICAL | 2 |
55
+ | HIGH | 9 |
56
+ | MEDIUM | 16 |
57
+ | LOW | 14 |
58
+ | **Total** | **41** |
59
+
60
+ ---
61
+
62
+ ## CRITICAL
63
+
64
+ ### 1. Hardcoded "60" skill count in metadata will drift on every skill addition
65
+
66
+ **Files:** `app/layout.tsx:21,61,75,190` · `app/cli/page.tsx:7`
67
+ **Category:** SEO / Maintenance
68
+
69
+ The string `"60 agent skills"` is hardcoded in 5 places across meta description, OpenGraph, Twitter card, and SoftwareApplication JSON-LD. The homepage body correctly uses `{skills.length}` and the CLI page body uses `{skillCount}`, but all `<meta>` descriptions are static strings. Every skill addition requires a multi-file find-and-replace — a maintenance trap that guarantees stale SEO data.
70
+
71
+ **Fix:** Derive the count from `getSkills().length` in metadata generation, or remove specific numbers from static meta strings.
72
+
73
+ ### 2. `install.sh` `--dir` flag has no input validation — path traversal
74
+
75
+ **File:** `public/install.sh:44-46`
76
+ **Category:** Security
77
+
78
+ The `--skill` flag correctly validates against `^[a-z0-9-]+$`, but the `--dir` flag accepts any arbitrary string with zero validation. An attacker could pass `--dir /etc/cron.d` or `--dir ../../etc` to write files to arbitrary directories.
79
+
80
+ ```bash
81
+ --dir)
82
+ TARGET_DIR="$2" # no sanitization
83
+ shift 2
84
+ ;;
85
+ ```
86
+
87
+ **Fix:** Validate that the directory is a relative path, reject paths containing `..`, and ensure it resolves within the working directory.
88
+
89
+ ---
90
+
91
+ ## HIGH
92
+
93
+ ### 2. Dead `useNpmDownloads()` hook — 22 lines of unused client code
94
+
95
+ **File:** `components/SkillsGrid.tsx:8-29`
96
+ **Category:** Dead Code / Bundle Size
97
+
98
+ A complete custom hook `useNpmDownloads()` is defined (with `useState`, `useEffect`, `fetch`, `sessionStorage` caching) but never called anywhere. The identical logic already lives in `components/NpmDownloads.tsx` which is the component actually used. This is dead code that inflates the client bundle.
99
+
100
+ **Fix:** Delete lines 8-29 from `SkillsGrid.tsx`.
101
+
102
+ ### 3. BreadcrumbList schema is static and incorrect on 60+ pages
103
+
104
+ **File:** `app/layout.tsx:205-240`
105
+ **Category:** SEO / Structured Data
106
+
107
+ The BreadcrumbList JSON-LD is injected in the root layout and always shows the same 4 items (Home → Skills → Docs → CLI). This renders on every page including `/faq`, all 60 `/skills/[name]` pages, and the 404. A BreadcrumbList should represent the navigational path to the *current* page. Google may show incorrect breadcrumbs in SERPs for skill pages.
108
+
109
+ **Fix:** Move breadcrumb generation to individual pages or generate dynamically based on route.
110
+
111
+ ### 4. FaqAccordion has zero ARIA attributes — WCAG 4.1.2 failure
112
+
113
+ **File:** `components/FaqAccordion.tsx:17-34`
114
+ **Category:** Accessibility
115
+
116
+ The accordion buttons lack `aria-expanded`, `aria-controls`, and `id`. The panels lack `role="region"`, `aria-labelledby`, and `id`. Screen readers cannot determine which items are expanded or collapsed. This is a WCAG 2.1 Level A violation.
117
+
118
+ **Fix:** Add `aria-expanded={open === i}`, `aria-controls={`faq-panel-${i}`}` to buttons. Add `id`, `role="region"`, `aria-labelledby` to panels.
119
+
120
+ ### 5. `<nav>` has no accessible label
121
+
122
+ **File:** `app/layout.tsx:111`
123
+ **Category:** Accessibility
124
+
125
+ The `<nav>` element has no `aria-label`. If a page has multiple nav landmarks, screen reader users cannot distinguish them.
126
+
127
+ **Fix:** Add `aria-label="Main navigation"`.
128
+
129
+ ### 6. `softwareVersion` in JSON-LD conflicts with `skills.json` version
130
+
131
+ **File:** `app/layout.tsx:188`
132
+ **Category:** SEO / Structured Data
133
+
134
+ The SoftwareApplication schema says `softwareVersion: "0.1.0"` (matching `package.json`), but all 60 skills in `skills.json` say `"version": "1.0.0"`. These are different things (CLI version vs skill version), but the inconsistency could confuse search engines. No single source of truth.
135
+
136
+ **Fix:** Import version from `package.json` dynamically for the CLI version.
137
+
138
+ ### 7. `.gitignore` is dangerously minimal — missing `.env*` patterns
139
+
140
+ **File:** `.gitignore`
141
+ **Category:** Security
142
+
143
+ The entire `.gitignore` is only 3 lines (`node_modules/`, `.next/`, `out/`). Critical missing entries:
144
+
145
+ | Missing pattern | Risk |
146
+ |----------------|------|
147
+ | `.env` / `.env.*` / `.env.local` | **Secrets could be committed accidentally** |
148
+ | `*.pem` / `*.key` | SSL/signing keys leaked |
149
+ | `.vercel` | Deployment cache with tokens |
150
+ | `.DS_Store` / `Thumbs.db` | OS artifacts |
151
+ | `coverage/` / `*.log` | Build artifacts |
152
+
153
+ While no `.env` files currently exist in the repo, any developer who creates one could accidentally commit secrets.
154
+
155
+ **Fix:** Add at minimum: `.env*`, `*.pem`, `*.key`, `.vercel`, `.DS_Store`.
156
+
157
+ ### 8. `install.sh` unknown arguments silently ignored — typos cause full install
158
+
159
+ **File:** `public/install.sh:48-50`
160
+ **Category:** UX / Safety
161
+
162
+ The wildcard case `*)` silently discards any unknown flag. If a user typos `--skiil seo-geo`, the flag is consumed and the script installs ALL 60 skills instead of the intended one.
163
+
164
+ **Fix:** Print an error and exit on unrecognized arguments.
165
+
166
+ ### 9. `install.sh` missing argument guard on `--skill`/`--dir`
167
+
168
+ **File:** `public/install.sh:40-47`
169
+ **Category:** Robustness
170
+
171
+ If a user runs `bash install.sh --skill` (without a value), `$2` is unset. Under `set -u`, this produces a cryptic "unbound variable" error instead of a helpful message.
172
+
173
+ **Fix:** Check `$# -ge 2` before accessing `$2`.
174
+
175
+ ### 10. 18 skills have content drift between SKILL.md and skills.json
176
+
177
+ **Files:** 18 skill directories in `skills-data/`
178
+ **Category:** Data Integrity
179
+
180
+ 18 of 60 skills have SKILL.md bodies that differ from their `content` field in `skills.json`. The pattern is consistent: SKILL.md files contain markdown hyperlinks (e.g., `[references/technical-seo.md](references/technical-seo.md)`) while `skills.json` has plain text references.
181
+
182
+ **Affected skills:** seo-geo, content-strategy, copywriting, page-cro, email-sequence, paid-ads, signup-flow-cro, popup-cro, programmatic-seo, growth-hacking, landing-page-builder, lead-scoring, local-seo, marketing-analytics, crm-builder, sales-funnel, smart-contract-auditor, social-media-kit.
183
+
184
+ **Fix:** Decide on a single source of truth (SKILL.md or skills.json) and create a build step to keep them in sync.
185
+
186
+ ---
187
+
188
+ ## MEDIUM
189
+
190
+ ### 11. No `<main>` landmark wrapping page content
191
+
192
+ **File:** `app/layout.tsx:132`
193
+ **Category:** Accessibility
194
+
195
+ `{children}` is wrapped in `<div className="relative z-10">` instead of `<main>`. Screen readers use the `<main>` landmark to skip navigation (WCAG 2.4.1).
196
+
197
+ **Fix:** Change `<div>` to `<main>`.
198
+
199
+ ### 12. Search input has no label
200
+
201
+ **File:** `components/SkillsGrid.tsx:68-76`
202
+ **Category:** Accessibility
203
+
204
+ The search `<input>` has a placeholder but no `<label>`, `aria-label`, or `aria-labelledby`. Placeholder is not a substitute for a label (WCAG 1.3.1, 3.3.2).
205
+
206
+ **Fix:** Add `aria-label="Search skills"`.
207
+
208
+ ### 13. Category filter buttons lack `aria-pressed` state
209
+
210
+ **File:** `components/SkillsGrid.tsx:80-102`
211
+ **Category:** Accessibility
212
+
213
+ Filter buttons change visual style when selected but have no `aria-pressed` or `aria-current` to communicate active state to assistive technology.
214
+
215
+ **Fix:** Add `aria-pressed={filter === cat}` to each button.
216
+
217
+ ### 14. Collapsed accordion content is in the accessibility tree
218
+
219
+ **File:** `components/FaqAccordion.tsx:26-28`
220
+ **Category:** Accessibility
221
+
222
+ Hidden content uses `maxHeight: "0px"` with `overflow: hidden`, but remains in the accessibility tree. Screen readers will read collapsed answers.
223
+
224
+ **Fix:** Add `aria-hidden={open !== i}` to collapsed panels.
225
+
226
+ ### 15. Markdown `h1` creates duplicate `<h1>` on skill pages
227
+
228
+ **File:** `components/SkillContent.tsx:13`
229
+ **Category:** SEO
230
+
231
+ The markdown renderer maps `h1` to `<h1>`. Skill pages already have an `<h1>` for the skill name (`app/skills/[name]/page.tsx:122`). When skill content starts with `# Title`, the page has two `<h1>` elements — an SEO violation.
232
+
233
+ **Fix:** Map markdown `h1` → `<h2>`, `h2` → `<h3>`, etc. in the SkillContent component.
234
+
235
+ ### 16. GA4 uses `dangerouslySetInnerHTML` instead of Next.js `<Script>`
236
+
237
+ **File:** `app/layout.tsx:103-108`
238
+ **Category:** Performance
239
+
240
+ The GA4 snippet is a render-blocking `<script>` in `<head>`. Next.js provides `<Script strategy="afterInteractive">` which defers loading for better performance and CSP compatibility.
241
+
242
+ **Fix:** Replace with `import Script from 'next/script'` and use `<Script strategy="afterInteractive">`.
243
+
244
+ ### 17. `NpmDownloads` returns `null` during loading — CLS issue
245
+
246
+ **File:** `components/NpmDownloads.tsx:27`
247
+ **Category:** UX / Performance
248
+
249
+ When `downloads` is `null` (before fetch completes), the component returns `null`. The parent stat block still renders the "npm downloads" label with an empty number area, causing Cumulative Layout Shift.
250
+
251
+ **Fix:** Return a placeholder (e.g., `"—"` or a skeleton) instead of `null`.
252
+
253
+ ### 18. `setTimeout` not cancelled on unmount in InstallBox
254
+
255
+ **File:** `components/InstallBox.tsx:11-12`
256
+ **Category:** React Anti-pattern
257
+
258
+ `setTimeout(() => setCopied(false), 1500)` is not cleaned up on unmount. Multiple rapid clicks also accumulate timers.
259
+
260
+ **Fix:** Store timeout in a `useRef`, clear on unmount and on subsequent clicks.
261
+
262
+ ### 19. Fetch calls don't check `response.ok` before `.json()`
263
+
264
+ **Files:** `components/NpmDownloads.tsx:16` · `components/SkillsGrid.tsx:18`
265
+ **Category:** Error Handling
266
+
267
+ Both fetch calls chain `.then(r => r.json())` without checking `r.ok`. Errors are silently swallowed by `.catch(() => {})`. No logging, no retry, no user feedback.
268
+
269
+ **Fix:** Add `if (!r.ok) throw new Error(r.statusText)` before `.json()`. Add at least `console.warn` in catch.
270
+
271
+ ### 20. Non-null assertions on canvas contexts
272
+
273
+ **File:** `components/AsciiBackground.tsx:36,42`
274
+ **Category:** Robustness
275
+
276
+ `getContext("2d")!` uses non-null assertions. While rare, `getContext` can return `null` in constrained environments (too many canvas contexts). Would cause unhandled TypeError.
277
+
278
+ **Fix:** Add null checks with early return, similar to the WebGL try/catch on line 24.
279
+
280
+ ### 21. Accordion max-height of 500px clips long answers on mobile
281
+
282
+ **File:** `components/FaqAccordion.tsx:28`
283
+ **Category:** UX
284
+
285
+ `maxHeight: "500px"` hard-caps expanded panel height. On narrow mobile screens, long FAQ answers will be clipped with no scroll.
286
+
287
+ **Fix:** Use CSS `grid-template-rows: 0fr/1fr` transitions, or measure content height with a ref.
288
+
289
+ ### 22. `react-markdown` v10 `li` component API may not pass `ordered`/`index` props
290
+
291
+ **File:** `components/SkillContent.tsx:27`
292
+ **Category:** Compatibility
293
+
294
+ The `li` component destructures `{ ordered, index }` props. In `react-markdown@^10.1.0`, these props may not be passed the same way as in v8/v9. If `ordered` is always `undefined`, ordered lists render with bullet style.
295
+
296
+ **Fix:** Verify against `react-markdown@10` docs and test ordered list rendering.
297
+
298
+ ### 23. AsciiBackground decorative canvas not hidden from screen readers
299
+
300
+ **File:** `components/AsciiBackground.tsx:197-203`
301
+ **Category:** Accessibility
302
+
303
+ The ASCII canvas is purely decorative but lacks `aria-hidden="true"` or `role="presentation"`. Screen readers may announce it.
304
+
305
+ **Fix:** Add `aria-hidden="true"` and `role="presentation"` to the outer `<div>`.
306
+
307
+ ### 24. Entire `skills.json` (357KB) bundled into homepage HTML
308
+
309
+ **File:** `lib/skills.ts` → `app/page.tsx`
310
+ **Category:** Performance
311
+
312
+ `lib/skills.ts` imports the full `skills.json` at module scope. Since Next.js static export serializes all data into the page HTML, the full markdown `content` fields for all 60 skills are embedded in `index.html` even though the homepage never renders skill content.
313
+
314
+ **Fix:** Split data — keep a lightweight index for the homepage, load full content only on skill detail pages.
315
+
316
+ ---
317
+
318
+ ## LOW
319
+
320
+ ### 25. `install.sh` silent failure when no JSON parser available
321
+
322
+ **File:** `public/install.sh:59-71`
323
+ **Category:** UX
324
+
325
+ If neither `python3` nor `node` is installed, `$SKILLS` is empty and the `for skill in $SKILLS` loop silently does nothing. The script prints "Done. Installed 0 skills" with no explanation.
326
+
327
+ **Fix:** Check that `$SKILLS` is non-empty after parsing; print an error if both parsers are missing.
328
+
329
+ ### 26. `SECURITY.md` references wrong directory
330
+
331
+ **File:** `SECURITY.md:41`
332
+ **Category:** Documentation
333
+
334
+ States "Skill content in `skills/` directory" but the actual path is `skills-data/`.
335
+
336
+ **Fix:** Change `skills/` to `skills-data/`.
337
+
338
+ ---
339
+
340
+ ## LOW
341
+
342
+ ### 27. No `<h1>` on homepage
343
+
344
+ **File:** `app/page.tsx`
345
+ **Category:** SEO
346
+
347
+ The homepage has no `<h1>`. The ASCII `<pre>` is the visual title, but the first semantic heading is `<h2>` ("Security"). Every page should have exactly one `<h1>`.
348
+
349
+ **Fix:** Add a visually-hidden `<h1>` or make the subtitle an `<h1>`.
350
+
351
+ ### 28. ASCII art `<pre>` is read character-by-character by screen readers
352
+
353
+ **File:** `app/page.tsx:27-29`
354
+ **Category:** Accessibility
355
+
356
+ The ASCII banner has no `aria-hidden="true"`. Screen readers announce hundreds of box-drawing characters.
357
+
358
+ **Fix:** Add `aria-hidden="true"` to the `<pre>`.
359
+
360
+ ### 29. Sitemap `lastModified` is always current build time
361
+
362
+ **File:** `app/sitemap.ts:8`
363
+ **Category:** SEO
364
+
365
+ Every sitemap entry uses `new Date().toISOString()`. This defeats the purpose of `lastModified` — search engines may distrust timestamps or re-crawl unnecessarily.
366
+
367
+ **Fix:** Use git commit dates or static dates for pages that haven't changed.
368
+
369
+ ### 30. Hardcoded hex colors in FAQ `<Code>` component bypass theme
370
+
371
+ **File:** `app/faq/page.tsx:12`
372
+ **Category:** Code Quality
373
+
374
+ The `Code` component uses `bg-[#0a0a0a]`, `border-[#222]`, `text-[#00ff88]` instead of theme tokens (`bg-bg`, `border-border`, `text-accent`).
375
+
376
+ **Fix:** Use Tailwind theme classes for consistency.
377
+
378
+ ### 31. `"/"` keyboard shortcut doesn't guard all editable contexts
379
+
380
+ **File:** `components/SkillsGrid.tsx:42-53`
381
+ **Category:** UX
382
+
383
+ The global `"/"` shortcut only excludes `HTMLInputElement`. It doesn't check for `HTMLTextAreaElement`, `[contenteditable]`, or modifier keys.
384
+
385
+ **Fix:** Expand guard to include `HTMLTextAreaElement`, `HTMLSelectElement`, and `[contenteditable]`.
386
+
387
+ ### 32. FAQ items keyed by array index
388
+
389
+ **File:** `components/FaqAccordion.tsx:16`
390
+ **Category:** React
391
+
392
+ `key={i}` uses index. Since the FAQ tracks open state by index (`open === i`), reordering the array would show the wrong FAQ as expanded.
393
+
394
+ **Fix:** Use `key={faq.q}` for a stable key.
395
+
396
+ ### 33. `prefersReducedMotion` is checked once and not reactive
397
+
398
+ **File:** `components/AsciiBackground.tsx:17`
399
+ **Category:** UX
400
+
401
+ `matchMedia("(prefers-reduced-motion: reduce)").matches` is read once. If the user changes their system preference while the page is open, the animation won't respond.
402
+
403
+ **Fix:** Listen to `MediaQueryList.addEventListener("change", ...)`.
404
+
405
+ ### 34. Decorative dots throughout UI lack `aria-hidden`
406
+
407
+ **Files:** `app/page.tsx:41,60-73` · `components/SkillsGrid.tsx:132` · `app/docs/page.tsx:55-110`
408
+ **Category:** Accessibility
409
+
410
+ Colored dot `<span>` elements are decorative but not excluded from the accessibility tree.
411
+
412
+ **Fix:** Add `aria-hidden="true"` to dot spans.
413
+
414
+ ### 35. Category buttons lack per-category counts
415
+
416
+ **File:** `components/SkillsGrid.tsx:90-102`
417
+ **Category:** UX
418
+
419
+ The "All" button shows `All ({skills.length})` but individual category buttons show only the name with no count.
420
+
421
+ ### 36. ESLint suppression without explanation
422
+
423
+ **File:** `components/AsciiBackground.tsx:194`
424
+ **Category:** Code Quality
425
+
426
+ `// eslint-disable-next-line react-hooks/exhaustive-deps` suppresses the rule. The empty dependency array is intentional but the suppression should explain why.
427
+
428
+ ### 37. Checkbox `<input>` rendered as `<span>` in SkillContent
429
+
430
+ **File:** `components/SkillContent.tsx:89-93`
431
+ **Category:** Accessibility
432
+
433
+ Custom `input` renderer replaces actual checkboxes with a decorative `<span>`. No semantic role, no keyboard interactivity.
434
+
435
+ **Fix:** Use `role="checkbox"` and `aria-checked` on the span.
436
+
437
+ ### 38. `llms-full.txt` sourced from `skills.json` — misses SKILL.md link updates
438
+
439
+ **File:** `public/llms-full.txt`
440
+ **Category:** Data Integrity
441
+
442
+ `llms-full.txt` matches `skills.json` content fields exactly, which means the 18 skills with updated markdown links in their SKILL.md files have those links absent from `llms-full.txt`. Low priority since the content is equivalent — just without hyperlinks.
443
+
444
+ ### 39. `install.sh` `rmdir` fails silently on partially-written directories
445
+
446
+ **File:** `public/install.sh:95`
447
+ **Category:** Cleanup
448
+
449
+ If `curl` partially writes `SKILL.md` before failing, `rmdir "$SKILL_DIR"` fails because the directory is not empty. The `|| true` suppresses this, leaving orphan directories with incomplete files.
450
+
451
+ **Fix:** Use `rm -rf "$SKILL_DIR"` for cleanup on failure.
452
+
453
+ ### 40. No `test` or `format` script in package.json
454
+
455
+ **File:** `package.json`
456
+ **Category:** Code Quality
457
+
458
+ No `"test"` script is defined — `npm test` fails. No Prettier or formatting tooling for consistent code style.
459
+
460
+ ### 41. Next.js 14.2.35 is on a maintenance branch
461
+
462
+ **File:** `package.json`
463
+ **Category:** Dependencies
464
+
465
+ Next.js 14.2.x is a maintenance branch. Next.js 15 has been stable since late 2024 with Turbopack, React 19, and improved caching. The 14.2.x line has had multiple security advisories (SSRF, header injection). Not urgent for a static export site, but worth tracking.
466
+
467
+ ---
468
+
469
+ ## Data Integrity Summary
470
+
471
+ | Check | Status |
472
+ |-------|--------|
473
+ | skills-data directories | 60 |
474
+ | skills.json entries | 60 |
475
+ | llms.txt skills listed | 60 |
476
+ | llms-full.txt skills | 60 |
477
+ | public/skills.json entries | 60 |
478
+ | Directory ↔ skills.json match | 60/60 (all match) |
479
+ | SKILL.md ↔ skills.json content | 42/60 exact, 18 have link drift |
480
+ | robots.txt domain | skills.ws (correct) |
481
+ | Sitemap domain | skills.ws (correct) |
482
+ | install.sh domain | skills.ws (correct) |
483
+ | install.sh `--skill` validation | Present (regex `^[a-z0-9-]+$`) |
484
+ | install.sh `--dir` validation | **Missing** — path traversal possible |
485
+
486
+ ### Category Distribution
487
+
488
+ | Category | Count |
489
+ |----------|-------|
490
+ | marketing | 15 |
491
+ | dev | 12 |
492
+ | conversion | 8 |
493
+ | growth | 8 |
494
+ | analytics | 7 |
495
+ | operations | 6 |
496
+ | design | 3 |
497
+ | web3 | 1 |
498
+
499
+ ---
500
+
501
+ ## Previously Fixed (this branch)
502
+
503
+ These issues from the prior audit (2026-02-23) have been resolved:
504
+
505
+ | Issue | Status |
506
+ |-------|--------|
507
+ | Stale `public/skills.json` (37 vs 60 skills) | Fixed — synced |
508
+ | FAQ schema JSX fallback to question text | Fixed — added `schemaA` field |
509
+ | `robots.txt` pointing to vercel subdomain | Fixed — `skills.ws` |
510
+ | FAQ page listing only 4 of 8 categories | Fixed — all 8 listed |
511
+ | Empty verification meta tags | Fixed — removed |
512
+ | `install.sh` path traversal via `--skill` | Fixed — regex validation |
513
+ | `install.sh` stale URLs | Fixed — `skills.ws` |
514
+ | `softwareVersion` mismatch with package.json | Fixed — `0.1.0` |
515
+ | AsciiBackground `ready` in useEffect deps | Fixed — removed |
516
+ | Missing `og.png` | Fixed — asset added |
package/SECURITY.md CHANGED
@@ -38,7 +38,7 @@ We will acknowledge receipt within 48 hours and provide a timeline for a fix.
38
38
  This policy covers:
39
39
  - The `skills-ws` npm package
40
40
  - The CLI tool (`npx skills-ws`)
41
- - Skill content in `skills/` directory
41
+ - Skill content in `skills-data/` directory
42
42
 
43
43
  This policy does NOT cover:
44
44
  - Third-party tools referenced in skill documentation (e.g., Google Analytics, VirusTotal)
@@ -0,0 +1,100 @@
1
+ import type { Metadata } from "next";
2
+ import { getSkills } from "@/lib/skills";
3
+
4
+ const skillCount = getSkills().length;
5
+
6
+ export const metadata: Metadata = {
7
+ title: "CLI Reference — npx skills-ws",
8
+ description: `Install agent skills from the command line with npx skills-ws. List, install, and manage ${skillCount} skills for AI coding assistants.`,
9
+ alternates: { canonical: "https://skills.ws/cli" },
10
+ };
11
+
12
+ export default function CliPage() {
13
+ const skillCount = getSkills().length;
14
+
15
+ return (
16
+ <div className="max-w-[700px] mx-auto px-6 py-16">
17
+ <h1 className="text-2xl font-bold font-sans text-text-main mb-8">CLI Reference</h1>
18
+
19
+ <section className="mb-10">
20
+ <h2 className="text-lg font-semibold text-text-main font-sans mb-3">Install all skills</h2>
21
+ <div className="bg-bg border border-border rounded-lg px-5 py-3 font-mono text-[13px] mb-3">
22
+ <span className="text-accent select-none">$ </span>
23
+ <span className="text-text-main">npx skills-ws</span>
24
+ </div>
25
+ <p className="text-[14px] text-text-dim font-sans leading-relaxed">
26
+ Installs all {skillCount} skills into your project. Works with any SKILL.md-compatible agent.
27
+ </p>
28
+ </section>
29
+
30
+ <section className="mb-10">
31
+ <h2 className="text-lg font-semibold text-text-main font-sans mb-3">Install a single skill</h2>
32
+ <div className="bg-bg border border-border rounded-lg px-5 py-3 font-mono text-[13px] mb-3">
33
+ <span className="text-accent select-none">$ </span>
34
+ <span className="text-text-main">npx skills-ws --skill seo-geo</span>
35
+ </div>
36
+ <p className="text-[14px] text-text-dim font-sans leading-relaxed">
37
+ Install only the skill you need. Replace <code className="bg-bg border border-border rounded px-1.5 py-0.5 text-[13px] font-mono text-accent">seo-geo</code> with any skill name.
38
+ </p>
39
+ </section>
40
+
41
+ <section className="mb-10">
42
+ <h2 className="text-lg font-semibold text-text-main font-sans mb-3">What gets installed</h2>
43
+ <p className="text-[14px] text-text-dim font-sans leading-relaxed mb-3">
44
+ Skills are added to your project directory. The exact location depends on your agent:
45
+ </p>
46
+ <div className="bg-bg border border-border rounded-lg p-5 font-mono text-[13px] text-text-dim space-y-1">
47
+ <div><span className="text-text-muted">Claude Code:</span> <span className="text-text-main">.claude/skills/</span></div>
48
+ <div><span className="text-text-muted">OpenClaw:</span> <span className="text-text-main">~/openclaw/skills/</span></div>
49
+ <div><span className="text-text-muted">Cursor:</span> <span className="text-text-main">.cursor/skills/</span></div>
50
+ <div><span className="text-text-muted">Codex:</span> <span className="text-text-main">.codex/skills/</span></div>
51
+ </div>
52
+ </section>
53
+
54
+ <section className="mb-10">
55
+ <h2 className="text-lg font-semibold text-text-main font-sans mb-3">SKILL.md format</h2>
56
+ <p className="text-[14px] text-text-dim font-sans leading-relaxed mb-4">
57
+ Every skill follows the same structure. The frontmatter tells the agent when to activate,
58
+ the body tells it what to do:
59
+ </p>
60
+ <div className="bg-bg border border-border rounded-lg p-5 font-mono text-[13px] text-text-main overflow-x-auto">
61
+ <pre>{`---
62
+ name: seo-geo
63
+ description: "SEO & GEO optimization for websites.
64
+ Use when the user wants to improve search
65
+ visibility, audit SEO, or optimize for AI
66
+ search engines."
67
+ ---
68
+
69
+ # SEO & GEO Optimization
70
+
71
+ ## Initial Assessment
72
+ Understand the site context before auditing:
73
+ - What type of site? (SaaS, e-commerce, blog)
74
+ - What keywords are priorities?
75
+ - Current organic traffic level?
76
+
77
+ ## Technical SEO Audit
78
+ ### Crawlability
79
+ - Check robots.txt for unintentional blocks
80
+ - Verify XML sitemap exists and is submitted
81
+ ...`}</pre>
82
+ </div>
83
+ </section>
84
+
85
+ <section className="mb-10">
86
+ <h2 className="text-lg font-semibold text-text-main font-sans mb-3">Compatible agents</h2>
87
+ <p className="text-[14px] text-text-dim font-sans leading-relaxed mb-3">
88
+ These skills work with any agent that supports the SKILL.md standard:
89
+ </p>
90
+ <div className="grid grid-cols-2 gap-2 max-sm:grid-cols-1">
91
+ {["OpenClaw", "Claude Code", "Cursor", "Codex", "Gemini CLI", "Any SKILL.md agent"].map((agent) => (
92
+ <div key={agent} className="bg-bg-card border border-border rounded-lg px-4 py-2.5 text-[13px] text-text-dim font-sans">
93
+ {agent}
94
+ </div>
95
+ ))}
96
+ </div>
97
+ </section>
98
+ </div>
99
+ );
100
+ }