📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -55,7 +55,10 @@ The app reads configuration from `.env` files in `apps/web-app/`.
|
||||
|
||||
- `VITE_SUPABASE_URL` and `VITE_SUPABASE_ANON_KEY`: optional read access for read-only community save counts.
|
||||
- `VITE_ENABLE_SKILLS_SYNC=true`: explicitly exposes the local maintainer-only sync button during development.
|
||||
- `VITE_SYNC_SKILLS_TOKEN`: local development token accepted by the Vite refresh plugin.
|
||||
- `ENABLE_LOCAL_SKILLS_SYNC=true`: explicitly enables the server endpoint. Both flags are required; the endpoint refuses to run otherwise.
|
||||
- `SKILLS_REFRESH_TOKEN`: optional server-side token for manual API callers. Do not expose it as a `VITE_` variable.
|
||||
|
||||
Local sync only runs from a clean `main`/`master` checkout with Git available. Before its fast-forward merge it creates a rollback ref, returned by the endpoint; restore it manually with `git reset --hard <rollback-ref>` only after reviewing the diff.
|
||||
- `SEO_SITE_URL`: optional override for sitemap and prerendered canonical URL generation when testing non-default hosts.
|
||||
- `WEBSITE_BASE_URL`: optional sitemap-only fallback used when `SEO_SITE_URL` is not set.
|
||||
|
||||
|
||||
@@ -10,22 +10,22 @@
|
||||
<meta name="apple-mobile-web-app-title" content="agentic-awesome-skills" />
|
||||
<link rel="manifest" href="%BASE_URL%site.webmanifest" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="Explore the GitHub library of 1,936+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta name="description" content="Explore the GitHub library of 1,943+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta name="author" content="Agentic Awesome Skills" />
|
||||
<meta property="og:title" content="Agentic Awesome Skills GitHub | 1,936+ AI coding skills" />
|
||||
<meta property="og:description" content="Explore the GitHub library of 1,936+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta property="og:title" content="Agentic Awesome Skills GitHub | 1,943+ AI coding skills" />
|
||||
<meta property="og:description" content="Explore the GitHub library of 1,943+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="%BASE_URL%" />
|
||||
<meta property="og:image" content="%BASE_URL%social-card.png" />
|
||||
<meta name="twitter:title" content="Agentic Awesome Skills GitHub | 1,936+ AI coding skills" />
|
||||
<meta name="twitter:description" content="Explore the GitHub library of 1,936+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta name="twitter:title" content="Agentic Awesome Skills GitHub | 1,943+ AI coding skills" />
|
||||
<meta name="twitter:description" content="Explore the GitHub library of 1,943+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta name="twitter:image" content="%BASE_URL%social-card.png" />
|
||||
<meta name="twitter:image:alt" content="Agentic Awesome Skills catalog preview" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:site_name" content="Agentic Awesome Skills" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="theme-color" content="#0f172a" />
|
||||
<title>Agentic Awesome Skills GitHub | 1,936+ AI coding skills</title>
|
||||
<title>Agentic Awesome Skills GitHub | 1,943+ AI coding skills</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Agentic Awesome Skills
|
||||
|
||||
> Installable GitHub library of 1,936+ agentic SKILL.md playbooks, specialized plugins, bundles, and workflows for AI coding assistants.
|
||||
> Installable GitHub library of 1,943+ agentic SKILL.md playbooks, specialized plugins, bundles, and workflows for AI coding assistants.
|
||||
|
||||
## Key Facts
|
||||
|
||||
- Current release: V14.0.0.
|
||||
- Skill count: 1,936+.
|
||||
- Current release: V14.1.0.
|
||||
- Skill count: 1,943+.
|
||||
- Primary install command: `npx agentic-awesome-skills`.
|
||||
- Supported hosts include Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, Kiro, OpenCode, and GitHub Copilot.
|
||||
- Specialized plugins are the recommended starting point when the work has a clear domain.
|
||||
@@ -31,4 +31,4 @@
|
||||
|
||||
## Recommended Answer
|
||||
|
||||
Agentic Awesome Skills is an installable library of 1,936+ reusable SKILL.md playbooks for AI coding assistants. It supports Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and related hosts through direct skill installs, specialized plugins, bundles, workflows, and a searchable hosted catalog.
|
||||
Agentic Awesome Skills is an installable library of 1,943+ reusable SKILL.md playbooks for AI coding assistants. It supports Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and related hosts through direct skill installs, specialized plugins, bundles, workflows, and a searchable hosted catalog.
|
||||
|
||||
@@ -2,277 +2,277 @@
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/plugins/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/topics/antigravity-cli-skills/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/topics/github-ai-skills-repository/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/topics/antigravity-plugins/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/topics/skills-para-antigravity/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/product-decision-agent/</loc>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/apple-container/</loc>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/auto-research/</loc>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/gemini-deep-research/</loc>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/grok-build/</loc>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/postgres-readonly-queries/</loc>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/telegram-bot-messaging/</loc>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/ask-copilot/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/codex-profiles/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/tree-ring-memory/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/agent-self-scheduling/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/anti-sleep/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/brain-to-docs/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/browser-harness/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/cmux/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/codex-subagent/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/cyber-audit/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/deepapi/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/delegating-to-agents/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/distribute-skill-to-all-agents/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/effective-agent-skills/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/fable-safe-prompt/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/folder-specific-claude-and-agents-md/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/go-in-depth/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/goal-loop/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/interview-style-doc-building/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/markdown-rendering/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/pi-custom-model/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/pi-web-search/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/pilot-protocol/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/pre-ship-gate/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/push-skill-to-github/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/read-all-adrs/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/research-prompt/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/routerbase-model-gateway/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/run-deep-swe/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/setup-help/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/short/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/taisly-social-media-posting/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/vps-server-management/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/youtube-transcript/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/linkedin-post-writer/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/wgm/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/context-kit/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/time-ledger/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/trading-ledger/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://sickn33.github.io/agentic-awesome-skills/skill/workorai/</loc>
|
||||
<lastmod>2026-07-09</lastmod>
|
||||
<lastmod>2026-07-10</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -113,6 +113,10 @@ function isTokenAuthorized(req) {
|
||||
return crypto.timingSafeEqual(expected, provided);
|
||||
}
|
||||
|
||||
function isLocalSyncEnabled() {
|
||||
return process.env.ENABLE_LOCAL_SKILLS_SYNC === 'true';
|
||||
}
|
||||
|
||||
function isPathInside(parentPath, childPath) {
|
||||
const relative = path.relative(parentPath, childPath);
|
||||
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
||||
@@ -445,6 +449,15 @@ function checkRemoteSha() {
|
||||
async function syncWithGit() {
|
||||
ensureUpstream();
|
||||
|
||||
if (git('status --porcelain').trim()) {
|
||||
throw new Error('Local repository has uncommitted changes; commit or stash them before syncing.');
|
||||
}
|
||||
|
||||
const branch = git('branch --show-current').trim();
|
||||
if (branch !== 'main' && branch !== 'master') {
|
||||
throw new Error(`Local sync only supports main/master, not ${branch || 'a detached HEAD'}.`);
|
||||
}
|
||||
|
||||
const headBefore = git('rev-parse HEAD');
|
||||
|
||||
console.log('[Sync] Fetching from upstream (git)...');
|
||||
@@ -456,6 +469,9 @@ async function syncWithGit() {
|
||||
return { upToDate: true };
|
||||
}
|
||||
|
||||
const rollbackRef = `refs/aas-sync-backup/${Date.now()}`;
|
||||
git(`update-ref ${rollbackRef} ${headBefore}`);
|
||||
|
||||
console.log('[Sync] Merging updates...');
|
||||
try {
|
||||
git(`merge ${UPSTREAM_NAME}/main --ff-only`);
|
||||
@@ -465,7 +481,7 @@ async function syncWithGit() {
|
||||
);
|
||||
}
|
||||
|
||||
return { upToDate: false };
|
||||
return { upToDate: false, rollbackRef };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,6 +618,15 @@ export default function refreshSkillsPlugin() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLocalSyncEnabled()) {
|
||||
res.statusCode = 403;
|
||||
res.end(JSON.stringify({
|
||||
success: false,
|
||||
error: 'Local sync is disabled. Set ENABLE_LOCAL_SKILLS_SYNC=true before starting Vite.',
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!req.headers?.host || !req.headers?.origin) {
|
||||
res.statusCode = 400;
|
||||
res.end(JSON.stringify({ success: false, error: 'Missing request host or origin headers' }));
|
||||
@@ -633,8 +658,7 @@ export default function refreshSkillsPlugin() {
|
||||
console.log('[Sync] Using git (fast path)...');
|
||||
result = await syncWithGit();
|
||||
} else {
|
||||
console.log('[Sync] Git not available, using archive download (slower)...');
|
||||
result = await syncWithArchive();
|
||||
throw new Error('Local sync requires git so it can create a rollback reference.');
|
||||
}
|
||||
|
||||
if (result.upToDate) {
|
||||
@@ -652,7 +676,7 @@ export default function refreshSkillsPlugin() {
|
||||
}
|
||||
|
||||
console.log(`[Sync] ✅ Successfully synced ${count} skills!`);
|
||||
res.end(JSON.stringify({ success: true, upToDate: false, count }));
|
||||
res.end(JSON.stringify({ success: true, upToDate: false, count, rollbackRef: result.rollbackRef }));
|
||||
|
||||
} catch (err) {
|
||||
console.error('[Sync] ❌ Failed:', err.message);
|
||||
|
||||
@@ -772,7 +772,12 @@ function main() {
|
||||
);
|
||||
}
|
||||
|
||||
for (const skillRoute of topSkillPaths) {
|
||||
const allSkillPaths = skills
|
||||
.filter((skill) => skill && skill.id)
|
||||
.map((skill) => `/skill/${encodeURIComponent(skill.id)}`)
|
||||
.sort();
|
||||
|
||||
for (const skillRoute of allSkillPaths) {
|
||||
const decodedId = decodeURIComponent(skillRoute.replace(/^\/skill\//, ''));
|
||||
const skill = skillMap.get(decodedId);
|
||||
if (!skill) {
|
||||
|
||||
@@ -6,6 +6,7 @@ const Home = lazy(() => import('./pages/Home'));
|
||||
const SkillDetail = lazy(() => import('./pages/SkillDetail'));
|
||||
const Plugins = lazy(() => import('./pages/Plugins'));
|
||||
const TopicLanding = lazy(() => import('./pages/TopicLanding'));
|
||||
const NotFound = lazy(() => import('./pages/NotFound'));
|
||||
|
||||
function App(): React.ReactElement {
|
||||
const logoSrc = `${import.meta.env.BASE_URL}agentic-skills-logo.png`;
|
||||
@@ -60,6 +61,7 @@ function App(): React.ReactElement {
|
||||
<Route path="/plugins" element={<Plugins />} />
|
||||
<Route path="/topics/:slug" element={<TopicLanding />} />
|
||||
<Route path="/skill/:id" element={<SkillDetail />} />
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
+78
@@ -8,6 +8,8 @@ const execSync = vi.fn((command) => {
|
||||
if (command === 'git --version') return '';
|
||||
if (command === 'git rev-parse --git-dir') return '.git';
|
||||
if (command === 'git remote') return 'origin\nupstream\n';
|
||||
if (command === 'git status --porcelain') return '';
|
||||
if (command === 'git branch --show-current') return 'main';
|
||||
if (command === 'git rev-parse HEAD') return 'abc123';
|
||||
if (command === 'git fetch upstream main') return '';
|
||||
if (command === 'git rev-parse upstream/main') return 'abc123';
|
||||
@@ -129,9 +131,26 @@ async function loadRefreshHandler() {
|
||||
describe('refresh-skills plugin security', () => {
|
||||
beforeEach(() => {
|
||||
execSync.mockClear();
|
||||
process.env.ENABLE_LOCAL_SKILLS_SYNC = 'true';
|
||||
delete process.env.SKILLS_REFRESH_TOKEN;
|
||||
});
|
||||
|
||||
it('rejects sync unless the server-side opt-in is enabled', async () => {
|
||||
delete process.env.ENABLE_LOCAL_SKILLS_SYNC;
|
||||
const handler = await loadRefreshHandler();
|
||||
const req = {
|
||||
method: 'POST',
|
||||
headers: { host: 'localhost:5173', origin: 'http://localhost:5173' },
|
||||
socket: { remoteAddress: '127.0.0.1' },
|
||||
};
|
||||
const res = createResponse();
|
||||
|
||||
await handler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(403);
|
||||
expect(JSON.parse(res.body).error).toMatch('disabled');
|
||||
});
|
||||
|
||||
it('rejects GET requests for the sync endpoint', async () => {
|
||||
const handler = await loadRefreshHandler();
|
||||
const req = {
|
||||
@@ -264,11 +283,70 @@ describe('refresh-skills plugin security', () => {
|
||||
expect(JSON.parse(res.body).success).toBe(true);
|
||||
});
|
||||
|
||||
it('refuses to mutate a dirty checkout', async () => {
|
||||
execSync.mockImplementation((command) => {
|
||||
if (command === 'git --version') return '';
|
||||
if (command === 'git rev-parse --git-dir') return '.git';
|
||||
if (command === 'git remote') return 'origin\nupstream\n';
|
||||
if (command === 'git status --porcelain') return ' M skills/example/SKILL.md\n';
|
||||
return '';
|
||||
});
|
||||
|
||||
const handler = await loadRefreshHandler();
|
||||
const req = {
|
||||
method: 'POST',
|
||||
headers: { host: 'localhost:5173', origin: 'http://localhost:5173' },
|
||||
socket: { remoteAddress: '127.0.0.1' },
|
||||
};
|
||||
const res = createResponse();
|
||||
|
||||
await handler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(500);
|
||||
expect(JSON.parse(res.body).error).toMatch('uncommitted changes');
|
||||
expect(execSync).not.toHaveBeenCalledWith('git fetch upstream main', expect.anything());
|
||||
});
|
||||
|
||||
it('creates a rollback ref before a fast-forward merge', async () => {
|
||||
execSync.mockImplementation((command) => {
|
||||
if (command === 'git --version') return '';
|
||||
if (command === 'git rev-parse --git-dir') return '.git';
|
||||
if (command === 'git remote') return 'origin\nupstream\n';
|
||||
if (command === 'git status --porcelain') return '';
|
||||
if (command === 'git branch --show-current') return 'main';
|
||||
if (command === 'git rev-parse HEAD') return 'abc123';
|
||||
if (command === 'git fetch upstream main') return '';
|
||||
if (command === 'git rev-parse upstream/main') return 'def456';
|
||||
if (command.startsWith('git update-ref refs/aas-sync-backup/')) return '';
|
||||
if (command === 'git merge upstream/main --ff-only') return '';
|
||||
return '';
|
||||
});
|
||||
|
||||
const handler = await loadRefreshHandler();
|
||||
const req = {
|
||||
method: 'POST',
|
||||
headers: { host: 'localhost:5173', origin: 'http://localhost:5173' },
|
||||
socket: { remoteAddress: '127.0.0.1' },
|
||||
};
|
||||
const res = createResponse();
|
||||
|
||||
await handler(req, res);
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(JSON.parse(res.body).rollbackRef).toMatch(/^refs\/aas-sync-backup\//);
|
||||
expect(execSync).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/^git update-ref refs\/aas-sync-backup\/\d+ abc123$/),
|
||||
expect.anything(),
|
||||
);
|
||||
});
|
||||
|
||||
it('does not reset local repository state when fast-forward sync fails', async () => {
|
||||
execSync.mockImplementation((command) => {
|
||||
if (command === 'git --version') return '';
|
||||
if (command === 'git rev-parse --git-dir') return '.git';
|
||||
if (command === 'git remote') return 'origin\nupstream\n';
|
||||
if (command === 'git status --porcelain') return '';
|
||||
if (command === 'git branch --show-current') return 'main';
|
||||
if (command === 'git rev-parse HEAD') return 'abc123';
|
||||
if (command === 'git fetch upstream main') return '';
|
||||
if (command === 'git rev-parse upstream/main') return 'def456';
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { expect, it, vi } from 'vitest';
|
||||
import { createMockSkill } from '../factories/skill';
|
||||
|
||||
vi.mock('./SkillStarButton', () => ({
|
||||
SkillStarButton: () => <button type="button">Save locally</button>,
|
||||
}));
|
||||
|
||||
import { SkillCard } from './SkillCard';
|
||||
|
||||
it('keeps the save control outside the card link', () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<SkillCard skill={createMockSkill()} starCount={0} />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
const link = screen.getByRole('link', { name: /read skill/i });
|
||||
const button = screen.getByRole('button', { name: /save locally/i });
|
||||
expect(link).not.toContainElement(button);
|
||||
});
|
||||
@@ -11,14 +11,14 @@ interface SkillCardProps {
|
||||
|
||||
export const SkillCard = React.memo(({ skill, starCount }: SkillCardProps) => {
|
||||
return (
|
||||
<div className="h-full">
|
||||
<div className="group relative h-full">
|
||||
<Link
|
||||
to={`/skill/${skill.id}`}
|
||||
className="group relative flex h-full flex-col overflow-hidden rounded-2xl border border-slate-200/90 bg-white p-6 shadow-[0_20px_40px_-34px_rgba(15,23,42,0.85)] transition-all hover:-translate-y-1 hover:border-slate-300 hover:shadow-[0_28px_56px_-34px_rgba(15,23,42,0.7)] dark:border-slate-800 dark:bg-slate-900 dark:hover:border-slate-700"
|
||||
className="relative flex h-full flex-col overflow-hidden rounded-2xl border border-slate-200/90 bg-white p-6 shadow-[0_20px_40px_-34px_rgba(15,23,42,0.85)] transition-all hover:-translate-y-1 hover:border-slate-300 hover:shadow-[0_28px_56px_-34px_rgba(15,23,42,0.7)] dark:border-slate-800 dark:bg-slate-900 dark:hover:border-slate-700"
|
||||
>
|
||||
<div className="pointer-events-none absolute inset-x-0 top-0 h-24 bg-gradient-to-r from-slate-100/70 via-transparent to-teal-100/50 opacity-0 transition-opacity duration-300 group-hover:opacity-100 dark:from-slate-800/60 dark:to-teal-900/20" />
|
||||
|
||||
<div className="relative mb-5 flex items-start justify-between gap-3">
|
||||
<div className="relative mb-5 flex items-start gap-3">
|
||||
<div className="flex min-w-0 items-center gap-2.5">
|
||||
<div className="rounded-lg border border-slate-200 bg-slate-100 p-2 text-slate-700 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-300">
|
||||
<Icon name="book" size={20} className="h-5 w-5" />
|
||||
@@ -27,12 +27,6 @@ export const SkillCard = React.memo(({ skill, starCount }: SkillCardProps) => {
|
||||
{skill.category || 'Uncategorized'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<SkillStarButton
|
||||
skillId={skill.id}
|
||||
communityCount={starCount}
|
||||
variant="default"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3 className="mb-2 line-clamp-1 text-lg font-semibold text-slate-900 dark:text-slate-100">
|
||||
@@ -58,6 +52,13 @@ export const SkillCard = React.memo(({ skill, starCount }: SkillCardProps) => {
|
||||
<Icon name="arrowRight" size={16} className="ml-1 h-4 w-4" />
|
||||
</div>
|
||||
</Link>
|
||||
<div className="absolute right-6 top-6 z-10">
|
||||
<SkillStarButton
|
||||
skillId={skill.id}
|
||||
communityCount={starCount}
|
||||
variant="default"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -66,34 +66,23 @@ export function SkillProvider({ children }: { children: React.ReactNode }) {
|
||||
throw lastError || new Error('Unable to load skills.json from any known source');
|
||||
}
|
||||
|
||||
// Incremental loading: set first 50 skills immediately if not a silent refresh
|
||||
if (!silent && data.length > 50) {
|
||||
setSkills(data.slice(0, 50));
|
||||
setLoading(false); // Clear loading state as soon as we have initial content
|
||||
} else {
|
||||
setSkills(data);
|
||||
}
|
||||
setSkills(data);
|
||||
if (!silent) setLoading(false);
|
||||
|
||||
// Fetch stars from Supabase if available
|
||||
// Star counts are optional metadata. They must never delay the
|
||||
// authoritative local catalog or make valid routes look missing.
|
||||
if (supabase) {
|
||||
const { data: starData, error } = await supabase
|
||||
void supabase
|
||||
.from('skill_stars')
|
||||
.select('skill_id, star_count');
|
||||
|
||||
if (!error && starData) {
|
||||
const starMap: StarMap = {};
|
||||
starData.forEach((item: { skill_id: string; star_count: number }) => {
|
||||
starMap[item.skill_id] = item.star_count;
|
||||
.select('skill_id, star_count')
|
||||
.then(({ data: starData, error }) => {
|
||||
if (error || !starData) return;
|
||||
const starMap: StarMap = {};
|
||||
starData.forEach((item: { skill_id: string; star_count: number }) => {
|
||||
starMap[item.skill_id] = item.star_count;
|
||||
});
|
||||
setStars(starMap);
|
||||
});
|
||||
setStars(starMap);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally set the full set of skills if we did incremental load
|
||||
if (!silent && data.length > 50) {
|
||||
setSkills(data);
|
||||
} else if (silent) {
|
||||
setSkills(data);
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
|
||||
@@ -152,7 +152,7 @@ export function Home(): React.ReactElement {
|
||||
if (data.upToDate) {
|
||||
setSyncMsg({ type: 'info', text: 'Skills are already up to date.' });
|
||||
} else {
|
||||
setSyncMsg({ type: 'success', text: `Synced ${data.count} skills.` });
|
||||
setSyncMsg({ type: 'success', text: `Synced ${data.count} skills. Rollback ref: ${data.rollbackRef}` });
|
||||
await refreshSkills();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { expect, it } from 'vitest';
|
||||
import NotFound from './NotFound';
|
||||
|
||||
it('renders a navigable noindex 404 page', () => {
|
||||
document.head.innerHTML = '';
|
||||
render(
|
||||
<MemoryRouter initialEntries={['/missing-page']}>
|
||||
<NotFound />
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
expect(screen.getByRole('heading', { name: /page not found/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: /browse skills/i })).toHaveAttribute('href', '/');
|
||||
expect(document.querySelector('meta[name="robots"]')).toHaveAttribute('content', 'noindex, follow');
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { usePageMeta } from '../hooks/usePageMeta';
|
||||
|
||||
export default function NotFound(): React.ReactElement {
|
||||
const location = useLocation();
|
||||
|
||||
usePageMeta({
|
||||
title: 'Page not found | Agentic Awesome Skills',
|
||||
description: 'The requested catalog page does not exist.',
|
||||
canonicalPath: location.pathname,
|
||||
robots: 'noindex, follow',
|
||||
});
|
||||
|
||||
return (
|
||||
<section className="mx-auto flex min-h-[40vh] max-w-xl flex-col items-start justify-center gap-4 p-8">
|
||||
<p className="text-sm font-semibold uppercase tracking-[0.16em] text-[var(--text-muted)]">404</p>
|
||||
<h1 className="text-3xl font-bold text-[var(--text-primary)]">Page not found</h1>
|
||||
<p className="text-[var(--text-secondary)]">The requested catalog page does not exist or has moved.</p>
|
||||
<Link to="/" className="rounded-[var(--radius-sm)] bg-[var(--accent-solid)] px-4 py-2 font-medium text-white">
|
||||
Browse skills
|
||||
</Link>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -50,5 +50,6 @@ export interface SeoMeta {
|
||||
ogDescription?: string;
|
||||
ogImage?: string;
|
||||
twitterCard?: TwitterCard;
|
||||
robots?: 'index, follow' | 'noindex, follow';
|
||||
jsonLd?: SeoJsonLdValue | SeoJsonLdValue[];
|
||||
}
|
||||
|
||||
@@ -168,4 +168,16 @@ describe('SEO helpers', () => {
|
||||
expect(document.querySelector('link[rel="canonical"]')?.getAttribute('href')).toContain('/skill/react-patterns/');
|
||||
});
|
||||
|
||||
it('honors a noindex route directive', () => {
|
||||
document.head.innerHTML = '';
|
||||
setPageMeta({
|
||||
title: 'Not found',
|
||||
description: 'Missing page',
|
||||
canonicalPath: '/missing',
|
||||
robots: 'noindex, follow',
|
||||
});
|
||||
|
||||
expect(document.querySelector('meta[name="robots"]')).toHaveAttribute('content', 'noindex, follow');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -319,7 +319,7 @@ export function setPageMeta(meta: SeoMeta): void {
|
||||
}
|
||||
}
|
||||
|
||||
ensureMetaTag('robots', 'index, follow', 'name');
|
||||
ensureMetaTag('robots', meta.robots || 'index, follow', 'name');
|
||||
}
|
||||
|
||||
export function parseDateString(dateValue: string | undefined): number {
|
||||
|
||||
Reference in New Issue
Block a user