[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6AG09WKNNPP6MHN14V8TM8C":3,"skill-same:01M6AG09WKNNPP6MHN14V8TM8C":145},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":26,"publishedAt":50,"linkOnly":51,"markdownContent":52,"markdownBody":53,"frontMatter":54,"refName":61,"commitSha":62,"skillDirectory":63,"forksCount":64,"repositoryHtmlUrl":65,"repositoryHomepageUrl":66,"ownerHtmlUrl":67,"ownerBlogUrl":68,"ownerDescription":69,"updatedAt":70,"install":71,"resources":84},"01M6AG09WKNNPP6MHN14V8TM8C","progressive-web-app",null,"Build Progressive Web Apps (PWAs) with offline support, installability, and caching strategies. Trigger whenever the user mentions PWA, service workers, web app manifests, Workbox, 'add to home screen', or wants their web app to work offline, feel native, or be installable.","davila7\u002Fclaude-code-templates","davila7","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6216945?v=4","01M6ADEWWFPS191NFM0Y1EX1JH","Daniel Avila","01M6ADJHGM78PHQRD3XZTFDVKK",33481,"MIT","https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fweb-development\u002Fprogressive-web-app\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49],"web","progressive web app","web app","app","web-dev","2026","manifests","native","screen","apps","worker","strategies","caching","service-worker","frontend","community","pwa","work","offline","dev","support","gemini","safe","2026-11-10 10:12:30",false,"---\nname: progressive-web-app\ndescription: \"Build Progressive Web Apps (PWAs) with offline support, installability, and caching strategies. Trigger whenever the user mentions PWA, service workers, web app manifests, Workbox, 'add to home screen', or wants their web app to work offline, feel native, or be installable.\"\nmetadata:\n  risk: safe\n  source: community\n  date_added: \"2026-03-17\"\n  tags: [pwa, web-dev, service-worker, frontend, offline, caching]\n  tools: [gemini, cursor, claude]\n---\n\n# Progressive Web Apps (PWAs)\n\n## Overview\n\nA Progressive Web App is a web application that uses modern browser capabilities to deliver a fast, reliable, and installable experience — even on unreliable networks. The three required pillars are:\n\n1. **HTTPS** — Required in production for service workers to register (localhost is exempt for development).\n2. **Web App Manifest** (`manifest.json`) — Makes the app installable and defines its appearance on device home screens.\n3. **Service Worker** (`sw.js`) — A background script that intercepts network requests, manages caches, and enables offline functionality.\n\n## When to Use This Skill\n\n- Use when the user wants their web app to work offline or on unreliable networks.\n- Use when building a mobile-first web project where users should be able to install the app to their home screen.\n- Use when the user asks about caching strategies, service workers, or improving web app performance and resilience.\n- Use when the user mentions Workbox, web app manifests, background sync, or push notifications for the web.\n- Use when the user asks \"can my website be installed like an app?\" or \"how do I make my site work offline?\" — even if they don't use the word PWA.\n\n## Deliverables Checklist\n\nEvery PWA implementation must include these files at minimum:\n\n- [ ] `index.html` — Links manifest, registers service worker\n- [ ] `manifest.json` — Full app metadata and icon set\n- [ ] `sw.js` — Service worker with install, activate, and fetch handlers\n- [ ] `app.js` — Main app logic with SW registration and install prompt handling\n- [ ] `offline.html` — Fallback page shown when navigation fails offline (required — missing file will cause install to fail)\n\n---\n\n## Step 1: Web App Manifest (`manifest.json`)\n\nDefines how the app appears when installed. Must be linked from `\u003Chead>` via `\u003Clink rel=\"manifest\">`.\n\n```json\n{\n  \"name\": \"My Awesome PWA\",\n  \"short_name\": \"MyPWA\",\n  \"description\": \"A fast, offline-capable Progressive Web App.\",\n  \"start_url\": \"\u002F\",\n  \"scope\": \"\u002F\",\n  \"display\": \"standalone\",\n  \"orientation\": \"portrait-primary\",\n  \"background_color\": \"#ffffff\",\n  \"theme_color\": \"#0055ff\",\n  \"icons\": [\n    {\n      \"src\": \"\u002Fassets\u002Ficons\u002Ficon-192x192.png\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image\u002Fpng\",\n      \"purpose\": \"any maskable\"\n    },\n    {\n      \"src\": \"\u002Fassets\u002Ficons\u002Ficon-512x512.png\",\n      \"sizes\": \"512x512\",\n      \"type\": \"image\u002Fpng\",\n      \"purpose\": \"any maskable\"\n    }\n  ],\n  \"screenshots\": [\n    {\n      \"src\": \"\u002Fassets\u002Fscreenshots\u002Fdesktop.png\",\n      \"sizes\": \"1280x720\",\n      \"type\": \"image\u002Fpng\",\n      \"form_factor\": \"wide\"\n    }\n  ]\n}\n```\n\n**Key fields:**\n- `display`: `standalone` hides browser UI; `minimal-ui` shows minimal controls; `browser` is standard tab.\n- `purpose: \"maskable\"` on icons enables adaptive icons on Android (safe zone matters — keep content in center 80%).\n- `screenshots` is optional but required for Chrome's enhanced install dialog on desktop.\n\n---\n\n## Step 2: HTML Shell (`index.html`)\n\n```html\n\u003C!DOCTYPE html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n  \u003Cmeta charset=\"UTF-8\">\n  \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  \u003Ctitle>My Awesome PWA\u003C\u002Ftitle>\n\n  \u003C!-- PWA manifest -->\n  \u003Clink rel=\"manifest\" href=\"\u002Fmanifest.json\">\n\n  \u003C!-- Theme color for browser chrome -->\n  \u003Cmeta name=\"theme-color\" content=\"#0055ff\">\n\n  \u003C!-- iOS-specific (Safari doesn't fully use manifest) -->\n  \u003Cmeta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n  \u003Cmeta name=\"apple-mobile-web-app-status-bar-style\" content=\"default\">\n  \u003Cmeta name=\"apple-mobile-web-app-title\" content=\"MyPWA\">\n  \u003Clink rel=\"apple-touch-icon\" href=\"\u002Fassets\u002Ficons\u002Ficon-192x192.png\">\n\n  \u003Clink rel=\"stylesheet\" href=\"\u002Fstyles.css\">\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv id=\"app\">\n    \u003Cheader>\u003Ch1>My PWA\u003C\u002Fh1>\u003C\u002Fheader>\n    \u003Cmain id=\"content\">Loading...\u003C\u002Fmain>\n    \u003C!-- Optional: install button, hidden by default -->\n    \u003Cbutton id=\"install-btn\" hidden>Install App\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cscript src=\"\u002Fapp.js\">\u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n```\n\n---\n\n## Step 3: Service Worker Registration & Install Prompt (`app.js`)\n\n```javascript\n\u002F\u002F ─── Service Worker Registration ───────────────────────────────────────────\nif ('serviceWorker' in navigator) {\n  window.addEventListener('load', async () => {\n    try {\n      const registration = await navigator.serviceWorker.register('\u002Fsw.js');\n      console.log('[App] SW registered, scope:', registration.scope);\n    } catch (err) {\n      console.error('[App] SW registration failed:', err);\n    }\n  });\n}\n\n\u002F\u002F ─── Install Prompt (Add to Home Screen) ───────────────────────────────────\nlet deferredPrompt;\nconst installBtn = document.getElementById('install-btn'); \u002F\u002F may be null if omitted\n\n\u002F\u002F Capture the browser's install prompt — it fires before the browser's own UI\nwindow.addEventListener('beforeinstallprompt', (e) => {\n  e.preventDefault(); \u002F\u002F Stop automatic mini-infobar on mobile\n  deferredPrompt = e;\n  if (installBtn) installBtn.hidden = false; \u002F\u002F Show your custom install button\n});\n\nif (installBtn) {\n  installBtn.addEventListener('click', async () => {\n    if (!deferredPrompt) return;\n    deferredPrompt.prompt();\n    const { outcome } = await deferredPrompt.userChoice;\n    console.log('[App] Install outcome:', outcome);\n    deferredPrompt = null;\n    installBtn.hidden = true;\n  });\n}\n\u002F\u002F Fires when the app is installed (via browser or your button)\nwindow.addEventListener('appinstalled', () => {\n  console.log('[App] PWA installed successfully');\n  installBtn.hidden = true;\n});\n```\n\n---\n\n## Step 4: Service Worker (`sw.js`)\n\n### Cache Versioning (critical — always increment on deploy)\n\n```javascript\nconst CACHE_VERSION = 'v1';\nconst STATIC_CACHE = `static-${CACHE_VERSION}`;\nconst DYNAMIC_CACHE = `dynamic-${CACHE_VERSION}`;\n\n\u002F\u002F Files to pre-cache during install (the \"App Shell\")\nconst APP_SHELL = [\n  '\u002F',\n  '\u002Findex.html',\n  '\u002Fstyles.css',\n  '\u002Fapp.js',\n  '\u002Fassets\u002Ficons\u002Ficon-192x192.png',\n  '\u002Foffline.html', \u002F\u002F Fallback page shown when network is unavailable\n];\n```\n\n### Install — Pre-cache the App Shell\n\n```javascript\nself.addEventListener('install', (event) => {\n  console.log('[SW] Installing...');\n  event.waitUntil(\n    caches.open(STATIC_CACHE).then((cache) => {\n      console.log('[SW] Pre-caching app shell');\n      return cache.addAll(APP_SHELL);\n    })\n  );\n  \u002F\u002F Activate immediately without waiting for old SW to die\n  self.skipWaiting();\n});\n```\n\n### Activate — Clean Up Old Caches\n\n```javascript\nself.addEventListener('activate', (event) => {\n  console.log('[SW] Activating...');\n  event.waitUntil(\n    caches.keys().then((cacheNames) => {\n      return Promise.all(\n        cacheNames\n          .filter((name) => name !== STATIC_CACHE && name !== DYNAMIC_CACHE)\n          .map((name) => {\n            console.log('[SW] Deleting old cache:', name);\n            return caches.delete(name);\n          })\n      );\n    })\n  );\n  \u002F\u002F Take control of all pages immediately\n  self.clients.claim();\n});\n```\n\n### Fetch — Caching Strategies\n\nChoose the right strategy per resource type:\n\n```javascript\nself.addEventListener('fetch', (event) => {\n  const { request } = event;\n  const url = new URL(request.url);\n\n  \u002F\u002F Only handle GET requests from our own origin\n  if (request.method !== 'GET' || url.origin !== location.origin) return;\n\n  \u002F\u002F Strategy A: Cache-First (for static assets — fast, tolerates stale)\n  if (url.pathname.match(\u002F\\.(css|js|png|jpg|svg|woff2)$\u002F)) {\n    event.respondWith(cacheFirst(request));\n    return;\n  }\n\n  \u002F\u002F Strategy B: Network-First (for HTML pages — fresh, falls back to cache)\n  if (request.headers.get('Accept')?.includes('text\u002Fhtml')) {\n    event.respondWith(networkFirst(request));\n    return;\n  }\n\n  \u002F\u002F Strategy C: Stale-While-Revalidate (for API data — fast and eventually fresh)\n  if (url.pathname.startsWith('\u002Fapi\u002F')) {\n    event.respondWith(staleWhileRevalidate(request));\n    return;\n  }\n});\n\n\u002F\u002F ─── Strategy Implementations ──────────────────────────────────────────────\n\nasync function cacheFirst(request) {\n  const cached = await caches.match(request);\n  if (cached) return cached;\n  try {\n    const response = await fetch(request);\n    const cache = await caches.open(STATIC_CACHE);\n    cache.put(request, response.clone());\n    return response;\n  } catch {\n    \u002F\u002F Nothing useful to fall back to for assets\n    return new Response('Asset unavailable offline', { status: 503 });\n  }\n}\n\nasync function networkFirst(request) {\n  try {\n    const response = await fetch(request);\n    const cache = await caches.open(DYNAMIC_CACHE);\n    cache.put(request, response.clone());\n    return response;\n  } catch {\n    const cached = await caches.match(request);\n    return cached || caches.match('\u002Foffline.html');\n  }\n}\n\nasync function staleWhileRevalidate(request) {\n  const cache = await caches.open(DYNAMIC_CACHE);\n  const cached = await cache.match(request);\n  const fetchPromise = fetch(request).then((response) => {\n    cache.put(request, response.clone());\n    return response;\n  });\n  return cached || fetchPromise;\n}\n```\n\n---\n\n## Edge Cases & Platform Notes\n\n### iOS \u002F Safari Quirks\n- Safari supports manifests and service workers but **does not support `beforeinstallprompt`** — users must install via the Share → \"Add to Home Screen\" menu manually.\n- Use the `apple-mobile-web-app-*` meta tags (shown in `index.html` above) for proper iOS integration.\n- Safari may clear service worker caches after ~7 days of inactivity (Intelligent Tracking Prevention).\n\n### HTTPS Requirement\n- Service workers only register on `https:\u002F\u002F` origins. `http:\u002F\u002Flocalhost` is the only exception for development.\n- Use a tool like `mkcert` or `ngrok` if you need HTTPS locally with a custom hostname.\n\n### Cache-Busting on Deploy\n- Always increment `CACHE_VERSION` in `sw.js` when deploying new assets. This ensures activate clears old caches and users get fresh files.\n- A common pattern is to inject the version automatically via your build tool (e.g., Vite, Webpack).\n\n### Opaque Responses (cross-origin requests)\n- Requests to external origins (e.g., CDN fonts, third-party APIs) return \"opaque\" responses that cannot be inspected. Cache them with caution — a failed opaque response still gets a `200` status.\n- Prefer `staleWhileRevalidate` for cross-origin resources, or use a library like Workbox which handles this safely.\n\n---\n\n## Workbox (Optional: Production Shortcut)\n\nFor production apps, consider [Workbox](https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox) (Google's PWA library) instead of hand-rolling strategies. It handles edge cases, cache expiry, and versioning automatically.\n\n```javascript\n\u002F\u002F With Workbox (via CDN for simplicity — use npm + bundler in production)\nimportScripts('https:\u002F\u002Fstorage.googleapis.com\u002Fworkbox-cdn\u002Freleases\u002F7.0.0\u002Fworkbox-sw.js');\n\nconst { registerRoute } = workbox.routing;\nconst { CacheFirst, NetworkFirst, StaleWhileRevalidate } = workbox.strategies;\nconst { precacheAndRoute } = workbox.precaching;\n\nprecacheAndRoute(self.__WB_MANIFEST || []); \u002F\u002F Injected by build plugin\n\nregisterRoute(({ request }) => request.destination === 'image', new CacheFirst());\nregisterRoute(({ request }) => request.mode === 'navigate', new NetworkFirst());\nregisterRoute(({ request }) => request.destination === 'script', new StaleWhileRevalidate());\n```\n\n---\n\n## Checklist Before Shipping\n\n- [ ] Site is served over HTTPS\n- [ ] `manifest.json` has `name`, `short_name`, `start_url`, `display`, `icons` (192 + 512)\n- [ ] Icons have `purpose: \"any maskable\"`\n- [ ] `sw.js` registers without errors in DevTools → Application → Service Workers\n- [ ] App shell loads from cache when network is throttled to \"Offline\" in DevTools\n- [ ] `offline.html` fallback is cached and served when navigation fails offline\n- [ ] Lighthouse PWA audit passes (Chrome DevTools → Lighthouse tab)\n- [ ] Tested on iOS Safari (manual install flow) and Android Chrome (install prompt)\n","# Progressive Web Apps (PWAs)\n\n## Overview\n\nA Progressive Web App is a web application that uses modern browser capabilities to deliver a fast, reliable, and installable experience — even on unreliable networks. The three required pillars are:\n\n1. **HTTPS** — Required in production for service workers to register (localhost is exempt for development).\n2. **Web App Manifest** (`manifest.json`) — Makes the app installable and defines its appearance on device home screens.\n3. **Service Worker** (`sw.js`) — A background script that intercepts network requests, manages caches, and enables offline functionality.\n\n## When to Use This Skill\n\n- Use when the user wants their web app to work offline or on unreliable networks.\n- Use when building a mobile-first web project where users should be able to install the app to their home screen.\n- Use when the user asks about caching strategies, service workers, or improving web app performance and resilience.\n- Use when the user mentions Workbox, web app manifests, background sync, or push notifications for the web.\n- Use when the user asks \"can my website be installed like an app?\" or \"how do I make my site work offline?\" — even if they don't use the word PWA.\n\n## Deliverables Checklist\n\nEvery PWA implementation must include these files at minimum:\n\n- [ ] `index.html` — Links manifest, registers service worker\n- [ ] `manifest.json` — Full app metadata and icon set\n- [ ] `sw.js` — Service worker with install, activate, and fetch handlers\n- [ ] `app.js` — Main app logic with SW registration and install prompt handling\n- [ ] `offline.html` — Fallback page shown when navigation fails offline (required — missing file will cause install to fail)\n\n---\n\n## Step 1: Web App Manifest (`manifest.json`)\n\nDefines how the app appears when installed. Must be linked from `\u003Chead>` via `\u003Clink rel=\"manifest\">`.\n\n```json\n{\n  \"name\": \"My Awesome PWA\",\n  \"short_name\": \"MyPWA\",\n  \"description\": \"A fast, offline-capable Progressive Web App.\",\n  \"start_url\": \"\u002F\",\n  \"scope\": \"\u002F\",\n  \"display\": \"standalone\",\n  \"orientation\": \"portrait-primary\",\n  \"background_color\": \"#ffffff\",\n  \"theme_color\": \"#0055ff\",\n  \"icons\": [\n    {\n      \"src\": \"\u002Fassets\u002Ficons\u002Ficon-192x192.png\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image\u002Fpng\",\n      \"purpose\": \"any maskable\"\n    },\n    {\n      \"src\": \"\u002Fassets\u002Ficons\u002Ficon-512x512.png\",\n      \"sizes\": \"512x512\",\n      \"type\": \"image\u002Fpng\",\n      \"purpose\": \"any maskable\"\n    }\n  ],\n  \"screenshots\": [\n    {\n      \"src\": \"\u002Fassets\u002Fscreenshots\u002Fdesktop.png\",\n      \"sizes\": \"1280x720\",\n      \"type\": \"image\u002Fpng\",\n      \"form_factor\": \"wide\"\n    }\n  ]\n}\n```\n\n**Key fields:**\n- `display`: `standalone` hides browser UI; `minimal-ui` shows minimal controls; `browser` is standard tab.\n- `purpose: \"maskable\"` on icons enables adaptive icons on Android (safe zone matters — keep content in center 80%).\n- `screenshots` is optional but required for Chrome's enhanced install dialog on desktop.\n\n---\n\n## Step 2: HTML Shell (`index.html`)\n\n```html\n\u003C!DOCTYPE html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n  \u003Cmeta charset=\"UTF-8\">\n  \u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  \u003Ctitle>My Awesome PWA\u003C\u002Ftitle>\n\n  \u003C!-- PWA manifest -->\n  \u003Clink rel=\"manifest\" href=\"\u002Fmanifest.json\">\n\n  \u003C!-- Theme color for browser chrome -->\n  \u003Cmeta name=\"theme-color\" content=\"#0055ff\">\n\n  \u003C!-- iOS-specific (Safari doesn't fully use manifest) -->\n  \u003Cmeta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n  \u003Cmeta name=\"apple-mobile-web-app-status-bar-style\" content=\"default\">\n  \u003Cmeta name=\"apple-mobile-web-app-title\" content=\"MyPWA\">\n  \u003Clink rel=\"apple-touch-icon\" href=\"\u002Fassets\u002Ficons\u002Ficon-192x192.png\">\n\n  \u003Clink rel=\"stylesheet\" href=\"\u002Fstyles.css\">\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Cdiv id=\"app\">\n    \u003Cheader>\u003Ch1>My PWA\u003C\u002Fh1>\u003C\u002Fheader>\n    \u003Cmain id=\"content\">Loading...\u003C\u002Fmain>\n    \u003C!-- Optional: install button, hidden by default -->\n    \u003Cbutton id=\"install-btn\" hidden>Install App\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cscript src=\"\u002Fapp.js\">\u003C\u002Fscript>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n```\n\n---\n\n## Step 3: Service Worker Registration & Install Prompt (`app.js`)\n\n```javascript\n\u002F\u002F ─── Service Worker Registration ───────────────────────────────────────────\nif ('serviceWorker' in navigator) {\n  window.addEventListener('load', async () => {\n    try {\n      const registration = await navigator.serviceWorker.register('\u002Fsw.js');\n      console.log('[App] SW registered, scope:', registration.scope);\n    } catch (err) {\n      console.error('[App] SW registration failed:', err);\n    }\n  });\n}\n\n\u002F\u002F ─── Install Prompt (Add to Home Screen) ───────────────────────────────────\nlet deferredPrompt;\nconst installBtn = document.getElementById('install-btn'); \u002F\u002F may be null if omitted\n\n\u002F\u002F Capture the browser's install prompt — it fires before the browser's own UI\nwindow.addEventListener('beforeinstallprompt', (e) => {\n  e.preventDefault(); \u002F\u002F Stop automatic mini-infobar on mobile\n  deferredPrompt = e;\n  if (installBtn) installBtn.hidden = false; \u002F\u002F Show your custom install button\n});\n\nif (installBtn) {\n  installBtn.addEventListener('click', async () => {\n    if (!deferredPrompt) return;\n    deferredPrompt.prompt();\n    const { outcome } = await deferredPrompt.userChoice;\n    console.log('[App] Install outcome:', outcome);\n    deferredPrompt = null;\n    installBtn.hidden = true;\n  });\n}\n\u002F\u002F Fires when the app is installed (via browser or your button)\nwindow.addEventListener('appinstalled', () => {\n  console.log('[App] PWA installed successfully');\n  installBtn.hidden = true;\n});\n```\n\n---\n\n## Step 4: Service Worker (`sw.js`)\n\n### Cache Versioning (critical — always increment on deploy)\n\n```javascript\nconst CACHE_VERSION = 'v1';\nconst STATIC_CACHE = `static-${CACHE_VERSION}`;\nconst DYNAMIC_CACHE = `dynamic-${CACHE_VERSION}`;\n\n\u002F\u002F Files to pre-cache during install (the \"App Shell\")\nconst APP_SHELL = [\n  '\u002F',\n  '\u002Findex.html',\n  '\u002Fstyles.css',\n  '\u002Fapp.js',\n  '\u002Fassets\u002Ficons\u002Ficon-192x192.png',\n  '\u002Foffline.html', \u002F\u002F Fallback page shown when network is unavailable\n];\n```\n\n### Install — Pre-cache the App Shell\n\n```javascript\nself.addEventListener('install', (event) => {\n  console.log('[SW] Installing...');\n  event.waitUntil(\n    caches.open(STATIC_CACHE).then((cache) => {\n      console.log('[SW] Pre-caching app shell');\n      return cache.addAll(APP_SHELL);\n    })\n  );\n  \u002F\u002F Activate immediately without waiting for old SW to die\n  self.skipWaiting();\n});\n```\n\n### Activate — Clean Up Old Caches\n\n```javascript\nself.addEventListener('activate', (event) => {\n  console.log('[SW] Activating...');\n  event.waitUntil(\n    caches.keys().then((cacheNames) => {\n      return Promise.all(\n        cacheNames\n          .filter((name) => name !== STATIC_CACHE && name !== DYNAMIC_CACHE)\n          .map((name) => {\n            console.log('[SW] Deleting old cache:', name);\n            return caches.delete(name);\n          })\n      );\n    })\n  );\n  \u002F\u002F Take control of all pages immediately\n  self.clients.claim();\n});\n```\n\n### Fetch — Caching Strategies\n\nChoose the right strategy per resource type:\n\n```javascript\nself.addEventListener('fetch', (event) => {\n  const { request } = event;\n  const url = new URL(request.url);\n\n  \u002F\u002F Only handle GET requests from our own origin\n  if (request.method !== 'GET' || url.origin !== location.origin) return;\n\n  \u002F\u002F Strategy A: Cache-First (for static assets — fast, tolerates stale)\n  if (url.pathname.match(\u002F\\.(css|js|png|jpg|svg|woff2)$\u002F)) {\n    event.respondWith(cacheFirst(request));\n    return;\n  }\n\n  \u002F\u002F Strategy B: Network-First (for HTML pages — fresh, falls back to cache)\n  if (request.headers.get('Accept')?.includes('text\u002Fhtml')) {\n    event.respondWith(networkFirst(request));\n    return;\n  }\n\n  \u002F\u002F Strategy C: Stale-While-Revalidate (for API data — fast and eventually fresh)\n  if (url.pathname.startsWith('\u002Fapi\u002F')) {\n    event.respondWith(staleWhileRevalidate(request));\n    return;\n  }\n});\n\n\u002F\u002F ─── Strategy Implementations ──────────────────────────────────────────────\n\nasync function cacheFirst(request) {\n  const cached = await caches.match(request);\n  if (cached) return cached;\n  try {\n    const response = await fetch(request);\n    const cache = await caches.open(STATIC_CACHE);\n    cache.put(request, response.clone());\n    return response;\n  } catch {\n    \u002F\u002F Nothing useful to fall back to for assets\n    return new Response('Asset unavailable offline', { status: 503 });\n  }\n}\n\nasync function networkFirst(request) {\n  try {\n    const response = await fetch(request);\n    const cache = await caches.open(DYNAMIC_CACHE);\n    cache.put(request, response.clone());\n    return response;\n  } catch {\n    const cached = await caches.match(request);\n    return cached || caches.match('\u002Foffline.html');\n  }\n}\n\nasync function staleWhileRevalidate(request) {\n  const cache = await caches.open(DYNAMIC_CACHE);\n  const cached = await cache.match(request);\n  const fetchPromise = fetch(request).then((response) => {\n    cache.put(request, response.clone());\n    return response;\n  });\n  return cached || fetchPromise;\n}\n```\n\n---\n\n## Edge Cases & Platform Notes\n\n### iOS \u002F Safari Quirks\n- Safari supports manifests and service workers but **does not support `beforeinstallprompt`** — users must install via the Share → \"Add to Home Screen\" menu manually.\n- Use the `apple-mobile-web-app-*` meta tags (shown in `index.html` above) for proper iOS integration.\n- Safari may clear service worker caches after ~7 days of inactivity (Intelligent Tracking Prevention).\n\n### HTTPS Requirement\n- Service workers only register on `https:\u002F\u002F` origins. `http:\u002F\u002Flocalhost` is the only exception for development.\n- Use a tool like `mkcert` or `ngrok` if you need HTTPS locally with a custom hostname.\n\n### Cache-Busting on Deploy\n- Always increment `CACHE_VERSION` in `sw.js` when deploying new assets. This ensures activate clears old caches and users get fresh files.\n- A common pattern is to inject the version automatically via your build tool (e.g., Vite, Webpack).\n\n### Opaque Responses (cross-origin requests)\n- Requests to external origins (e.g., CDN fonts, third-party APIs) return \"opaque\" responses that cannot be inspected. Cache them with caution — a failed opaque response still gets a `200` status.\n- Prefer `staleWhileRevalidate` for cross-origin resources, or use a library like Workbox which handles this safely.\n\n---\n\n## Workbox (Optional: Production Shortcut)\n\nFor production apps, consider [Workbox](https:\u002F\u002Fdeveloper.chrome.com\u002Fdocs\u002Fworkbox) (Google's PWA library) instead of hand-rolling strategies. It handles edge cases, cache expiry, and versioning automatically.\n\n```javascript\n\u002F\u002F With Workbox (via CDN for simplicity — use npm + bundler in production)\nimportScripts('https:\u002F\u002Fstorage.googleapis.com\u002Fworkbox-cdn\u002Freleases\u002F7.0.0\u002Fworkbox-sw.js');\n\nconst { registerRoute } = workbox.routing;\nconst { CacheFirst, NetworkFirst, StaleWhileRevalidate } = workbox.strategies;\nconst { precacheAndRoute } = workbox.precaching;\n\nprecacheAndRoute(self.__WB_MANIFEST || []); \u002F\u002F Injected by build plugin\n\nregisterRoute(({ request }) => request.destination === 'image', new CacheFirst());\nregisterRoute(({ request }) => request.mode === 'navigate', new NetworkFirst());\nregisterRoute(({ request }) => request.destination === 'script', new StaleWhileRevalidate());\n```\n\n---\n\n## Checklist Before Shipping\n\n- [ ] Site is served over HTTPS\n- [ ] `manifest.json` has `name`, `short_name`, `start_url`, `display`, `icons` (192 + 512)\n- [ ] Icons have `purpose: \"any maskable\"`\n- [ ] `sw.js` registers without errors in DevTools → Application → Service Workers\n- [ ] App shell loads from cache when network is throttled to \"Offline\" in DevTools\n- [ ] `offline.html` fallback is cached and served when navigation fails offline\n- [ ] Lighthouse PWA audit passes (Chrome DevTools → Lighthouse tab)\n- [ ] Tested on iOS Safari (manual install flow) and Android Chrome (install prompt)\n",{"name":5,"metadata":55,"description":7},{"risk":49,"tags":56,"tools":57,"source":42,"date_added":60},[43,31,40,41,45,39],[48,58,59],"cursor","claude","2026-03-17","main","2b749c82b5d11fac39d9a6e021ce3f5b8a541bd1","cli-tool\u002Fcomponents\u002Fskills\u002Fweb-development\u002Fprogressive-web-app",3931,"https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates","https:\u002F\u002Faitmpl.com","https:\u002F\u002Fgithub.com\u002Fdavila7","https:\u002F\u002Fdanielavila.me","Building AI dev tools with LLMs","2026-11-14 06:14:46",{"source":8,"skillName":5,"commands":72,"prompt":82,"zipAvailable":83},[73,76,79],{"tool":74,"command":75},"npx","npx skills add davila7\u002Fclaude-code-templates --skill progressive-web-app",{"tool":77,"command":78},"bunx","bunx skills add davila7\u002Fclaude-code-templates --skill progressive-web-app",{"tool":80,"command":81},"pnpm","pnpm dlx skills add davila7\u002Fclaude-code-templates --skill progressive-web-app","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fweb-development\u002Fprogressive-web-app\u002FSKILL.md",true,[85,91,95,99,103,107,111,117,121,125,129,133,137,141],{"path":86,"resourceType":87,"resourceTypeDesc":88,"sizeBytes":89,"sourceHtmlUrl":90},"mockups\u002Fchange-legend.tsx","OTHER","其他",1958,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Fmockups\u002Fchange-legend.tsx",{"path":92,"resourceType":87,"resourceTypeDesc":88,"sizeBytes":93,"sourceHtmlUrl":94},"mockups\u002Fcomponent-cards.tsx",4625,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Fmockups\u002Fcomponent-cards.tsx",{"path":96,"resourceType":87,"resourceTypeDesc":88,"sizeBytes":97,"sourceHtmlUrl":98},"mockups\u002Ffile-tree.tsx",1817,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Fmockups\u002Ffile-tree.tsx",{"path":100,"resourceType":87,"resourceTypeDesc":88,"sizeBytes":101,"sourceHtmlUrl":102},"mockups\u002Fhook-card.tsx",5246,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Fmockups\u002Fhook-card.tsx",{"path":104,"resourceType":87,"resourceTypeDesc":88,"sizeBytes":105,"sourceHtmlUrl":106},"mockups\u002Finterface-card.tsx",4296,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Fmockups\u002Finterface-card.tsx",{"path":108,"resourceType":87,"resourceTypeDesc":88,"sizeBytes":109,"sourceHtmlUrl":110},"mockups\u002Fstate-card.tsx",4947,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Fmockups\u002Fstate-card.tsx",{"path":112,"resourceType":113,"resourceTypeDesc":114,"sizeBytes":115,"sourceHtmlUrl":116},"references\u002Frepresentations\u002Fcallstack.md","REFERENCE","参考文档",1109,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Freferences\u002Frepresentations\u002Fcallstack.md",{"path":118,"resourceType":113,"resourceTypeDesc":114,"sizeBytes":119,"sourceHtmlUrl":120},"references\u002Frepresentations\u002Fcomponent-tree.md",3427,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Freferences\u002Frepresentations\u002Fcomponent-tree.md",{"path":122,"resourceType":113,"resourceTypeDesc":114,"sizeBytes":123,"sourceHtmlUrl":124},"references\u002Frepresentations\u002Fderivation-ledger.md",5262,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Freferences\u002Frepresentations\u002Fderivation-ledger.md",{"path":126,"resourceType":113,"resourceTypeDesc":114,"sizeBytes":127,"sourceHtmlUrl":128},"references\u002Frepresentations\u002Finterfaces.md",3097,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Freferences\u002Frepresentations\u002Finterfaces.md",{"path":130,"resourceType":113,"resourceTypeDesc":114,"sizeBytes":131,"sourceHtmlUrl":132},"references\u002Frepresentations\u002Fmodule-list.md",1508,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Freferences\u002Frepresentations\u002Fmodule-list.md",{"path":134,"resourceType":113,"resourceTypeDesc":114,"sizeBytes":135,"sourceHtmlUrl":136},"references\u002Frepresentations\u002Fpackage-layout.md",795,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Freferences\u002Frepresentations\u002Fpackage-layout.md",{"path":138,"resourceType":113,"resourceTypeDesc":114,"sizeBytes":139,"sourceHtmlUrl":140},"references\u002Frepresentations\u002Fscreen-layout.md",758,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Freferences\u002Frepresentations\u002Fscreen-layout.md",{"path":142,"resourceType":113,"resourceTypeDesc":114,"sizeBytes":143,"sourceHtmlUrl":144},"references\u002Frepresentations\u002Fstandards.md",359,"https:\u002F\u002Fgithub.com\u002Fasermax\u002Fclaude-plugins\u002Fblob\u002Fmaster\u002Fmahou\u002Fskills\u002Fprogram-design-with-canvas\u002Freferences\u002Frepresentations\u002Fstandards.md",[146,174,200,233,264,288],{"skillNo":147,"name":148,"nameZh":6,"description":149,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":150,"categories":151,"keywords":153,"publishedAt":50},"01M70NHBTQ8BV0A2SCAZXEPHCX","wealthville-pool-scoring","Score DeFi liquidity pools before providing liquidity. Returns a 0-100 pool score and an Enter\u002FHold\u002FExit\u002FReduce\u002FAvoid verdict with calibrated confidence for ~68,800 Solana pools and 575 EVM pools, plus a public miss-inclusive track record. Use when asked whether a pool is worth LPing into, which pools look best right now, or how accurate the scores have actually been.","https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fweb-data\u002Fwealthville-pool-scoring\u002FSKILL.md",[152],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173],"scoring","pool","wealthville pool scoring","liquidity","which","for","exit","now","use","when","reduce","with","record","track","verdict","evm","solana","how","public","look",{"skillNo":175,"name":176,"nameZh":6,"description":177,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":178,"categories":179,"keywords":181,"publishedAt":50},"01M70MB6VR8ERJ4EM8D5KHBMEV","technical-debt-auditor","Scans a codebase and produces a quantified technical debt report with a prioritized remediation plan. Analyzes dead code, TODO\u002FFIXME\u002FHACK markers, dependency staleness, complexity hotspots, code duplication signals, test coverage gaps, unused exports, and outdated patterns. Use this skill when the user asks to audit their codebase for tech debt, wants a health check of their code quality, says things like \"where should I focus refactoring\", \"find code smells\", \"audit my codebase\", \"what technical debt do we have\", \"prioritize cleanup work\", or asks about code quality metrics. Also trigger when the user mentions \"code rot\", \"legacy cleanup\", \"refactoring priorities\", or wants to understand the overall health of a project before a major effort. Trigger even if the user doesn't say \"technical debt\" explicitly — any request to systematically assess code quality and identify improvement priorities belongs here.","https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fdevelopment\u002Ftechnical-debt-auditor\u002FSKILL.md",[180],{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},[182,183,184,185,186,187,188,189,190,191,192,163,193,194,44,195,196,197,198,199],"technical debt","debt","technical","technical debt auditor","auditor","coverage","complexity","markers","project","cleanup","unused","code","todo","plan","focus","signals","audit","dependency",{"skillNo":201,"name":202,"nameZh":6,"description":203,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":204,"categories":205,"keywords":215,"publishedAt":50},"01M70KPYH9QNHCNME5GJQGE22K","readonly-sql-guard","Make an agent's database access provably read-only. Use when an agent runs caller-supplied SQL against a production\u002FERP\u002Fanalytics database (a \"query\" MCP tool, text-to-SQL, an admin console), or when reviewing DB-access code for write\u002FDDL\u002Fside-effect\u002Ftransaction-escape risks. Checks what a statement CALLS, not just its shape.","https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fdatabase\u002Freadonly-sql-guard\u002FSKILL.md",[206,209,210],{"categoryNo":207,"categoryName":208,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":211,"categoryName":212,"taxonomyType":213,"taxonomyTypeDesc":214},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[216,217,218,219,220,221,222,159,223,224,225,226,227,228,229,162,230,231,163,232],"readonly","guard","sql guard","sql","readonly sql guard","erp","effect","read-only","db","just","tool","calls","only","MCP","access","database","query",{"skillNo":234,"name":235,"nameZh":6,"description":236,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":237,"licenseFileUrl":238,"sourceHtmlUrl":239,"categories":240,"keywords":242,"publishedAt":50},"01M6AFY6F7CC2BBZP1QD6VTNVC","web-artifacts-builder","Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn\u002Fui). Use for complex artifacts requiring state management, routing, or shadcn\u002Fui components - not for simple single-file HTML\u002FJSX artifacts.","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FSKILL.md",[241],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[243,27,244,245,246,247,248,249,250,251,41,252,253,254,255,256,257,258,259,260,261,262,159,263],"artifacts builder","web artifacts builder","artifacts","builder","state","routing","state management","component","css","ui","Tools","file","components","shadcn","react","Tailwind","suite","jsx","html","single-file","ui components",{"skillNo":265,"name":266,"nameZh":6,"description":267,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":237,"licenseFileUrl":268,"sourceHtmlUrl":269,"categories":270,"keywords":275,"publishedAt":50},"01M6AFWF33SFV9Z3SF4PEEE4BF","webapp-testing","Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.","https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FSKILL.md",[271,274],{"categoryNo":272,"categoryName":273,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[276,277,278,279,280,27,281,41,252,282,283,284,285,159,286,165,287],"webapp testing","testing","webapp","browser","playwright","applications","debugging","toolkit","logs","screenshots","local","behavior",{"skillNo":289,"name":290,"nameZh":6,"description":291,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":237,"licenseFileUrl":292,"sourceHtmlUrl":293,"categories":294,"keywords":302,"publishedAt":50},"01M6AF67Q67JJJ5V51JR2P52KA","discernment-nudge","After you give a substantive answer or draft that the user may act on — advice or recommendations, drafted artifacts such as goals, plans, pitches, proposals, or emails, estimates or projections, analysis or interpretation of data, factual claims they may rely on, or a multi-step argument — invoke this skill BEFORE finalizing your reply and then, if it applies, append 2-3 short follow-up questions, each tied to something specific in what you just produced, that help the user check key facts, probe the reasoning or assumptions, and notice missing context. Do this at most once per conversation. Skip it when the user asked a trivial how-to or simple lookup, wants a purely educational explanation, asked you only to format, convert, or assemble a file from content they provided, is writing code they will run, is doing creative writing or casual chat, or already asked you to double-check, cite, or review — the skill file explains these boundaries and the exact output format.","https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fdiscernment-nudge\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fdavila7\u002Fclaude-code-templates\u002Fblob\u002Fmain\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fdiscernment-nudge\u002FSKILL.md",[295,298,299],{"categoryNo":296,"categoryName":297,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":300,"categoryName":301,"taxonomyType":213,"taxonomyTypeDesc":214},"01JBSEEDCAT000000000000036","内容创作者",[303,304,305,306,307,308,309,310,245,311,254,193,312,313,314,315,316,228,317,163,318,319,320,321,322,171,323],"discernment nudge","nudge","interpretation","help","run","reasoning","data","writing","lookup","content","review","conversation","short","up","invoke","you","analysis","claims","argument","assemble","recommendations"]