📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-07-03 16:04:10 +00:00
parent 2bf579321a
commit b40381458b
482 changed files with 8324 additions and 2007 deletions
@@ -6,12 +6,12 @@
},
"metadata": {
"description": "Claude Code marketplace entries for the plugin-safe Antigravity Awesome Skills library and its compatible editorial bundles.",
"version": "13.7.0"
"version": "13.9.0"
},
"plugins": [
{
"name": "antigravity-awesome-skills",
"version": "13.7.0",
"version": "13.9.0",
"description": "Expose the plugin-safe Claude Code subset of Antigravity Awesome Skills through a single marketplace entry.",
"author": {
"name": "sickn33 and contributors",
@@ -31,7 +31,7 @@
},
{
"name": "antigravity-bundle-essentials",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Essentials\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -51,7 +51,7 @@
},
{
"name": "antigravity-bundle-security-engineer",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Security Engineer\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -71,7 +71,7 @@
},
{
"name": "antigravity-bundle-security-developer",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Security Developer\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -91,7 +91,7 @@
},
{
"name": "antigravity-bundle-web-wizard",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Web Wizard\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -111,7 +111,7 @@
},
{
"name": "antigravity-bundle-web-designer",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Web Designer\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -131,7 +131,7 @@
},
{
"name": "antigravity-bundle-full-stack-developer",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Full-Stack Developer\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -151,7 +151,7 @@
},
{
"name": "antigravity-bundle-agent-architect",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Agent Architect\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -171,7 +171,7 @@
},
{
"name": "antigravity-bundle-llm-application-developer",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"LLM Application Developer\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -191,7 +191,7 @@
},
{
"name": "antigravity-bundle-indie-game-dev",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Indie Game Dev\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -211,7 +211,7 @@
},
{
"name": "antigravity-bundle-python-pro",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Python Pro\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -231,7 +231,7 @@
},
{
"name": "antigravity-bundle-typescript-javascript",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"TypeScript & JavaScript\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -251,7 +251,7 @@
},
{
"name": "antigravity-bundle-systems-programming",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Systems Programming\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -271,7 +271,7 @@
},
{
"name": "antigravity-bundle-startup-founder",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Startup Founder\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -291,7 +291,7 @@
},
{
"name": "antigravity-bundle-business-analyst",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Business Analyst\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -311,7 +311,7 @@
},
{
"name": "antigravity-bundle-marketing-growth",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Marketing & Growth\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -331,7 +331,7 @@
},
{
"name": "antigravity-bundle-devops-cloud",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"DevOps & Cloud\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -351,7 +351,7 @@
},
{
"name": "antigravity-bundle-observability-monitoring",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Observability & Monitoring\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -371,7 +371,7 @@
},
{
"name": "antigravity-bundle-data-analytics",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Data & Analytics\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -391,7 +391,7 @@
},
{
"name": "antigravity-bundle-data-engineering",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Data Engineering\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -411,7 +411,7 @@
},
{
"name": "antigravity-bundle-creative-director",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Creative Director\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -431,7 +431,7 @@
},
{
"name": "antigravity-bundle-qa-testing",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"QA & Testing\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -451,7 +451,7 @@
},
{
"name": "antigravity-bundle-aas-web-app-builder",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Web App Builder\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -471,7 +471,7 @@
},
{
"name": "antigravity-bundle-aas-product-design-studio",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Product Design Studio\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -491,7 +491,7 @@
},
{
"name": "antigravity-bundle-aas-security-engineer",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Security Engineer\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -511,7 +511,7 @@
},
{
"name": "antigravity-bundle-aas-secure-app-builder",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Secure App Builder\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -531,7 +531,7 @@
},
{
"name": "antigravity-bundle-aas-documents-presentations",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Documents & Presentations\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -551,7 +551,7 @@
},
{
"name": "antigravity-bundle-aas-data-analytics",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Data Analytics\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -571,7 +571,7 @@
},
{
"name": "antigravity-bundle-aas-agent-mcp-builder",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Agent & MCP Builder\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -591,7 +591,7 @@
},
{
"name": "antigravity-bundle-aas-oss-maintainer",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS OSS Maintainer\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -611,7 +611,7 @@
},
{
"name": "antigravity-bundle-aas-qa-test-automation",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS QA & Test Automation\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -631,7 +631,7 @@
},
{
"name": "antigravity-bundle-aas-devops-cloud",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS DevOps & Cloud\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -651,7 +651,7 @@
},
{
"name": "antigravity-bundle-aas-marketing-seo-growth",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Marketing, SEO & Growth\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -671,7 +671,7 @@
},
{
"name": "antigravity-bundle-aas-automation-builder",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Automation Builder\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -691,7 +691,7 @@
},
{
"name": "antigravity-bundle-aas-observability-ir",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Observability IR\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -711,7 +711,7 @@
},
{
"name": "antigravity-bundle-aas-python-api-builder",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Python API Builder\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -731,7 +731,7 @@
},
{
"name": "antigravity-bundle-aas-mobile-app-builder",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Mobile App Builder\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -751,7 +751,7 @@
},
{
"name": "antigravity-bundle-mobile-developer",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Mobile Developer\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -771,7 +771,7 @@
},
{
"name": "antigravity-bundle-integration-apis",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Integration & APIs\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -791,7 +791,7 @@
},
{
"name": "antigravity-bundle-architecture-design",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Architecture & Design\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -811,7 +811,7 @@
},
{
"name": "antigravity-bundle-ddd-evented-architecture",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"DDD & Evented Architecture\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -831,7 +831,7 @@
},
{
"name": "antigravity-bundle-automation-builder",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Automation Builder\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -851,7 +851,7 @@
},
{
"name": "antigravity-bundle-revops-crm-automation",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"RevOps & CRM Automation\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -871,7 +871,7 @@
},
{
"name": "antigravity-bundle-commerce-payments",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Commerce & Payments\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -891,7 +891,7 @@
},
{
"name": "antigravity-bundle-odoo-erp",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Odoo ERP\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -911,7 +911,7 @@
},
{
"name": "antigravity-bundle-azure-ai-cloud",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Azure AI & Cloud\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -931,7 +931,7 @@
},
{
"name": "antigravity-bundle-expo-react-native",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Expo & React Native\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -951,7 +951,7 @@
},
{
"name": "antigravity-bundle-apple-platform-design",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Apple Platform Design\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -971,7 +971,7 @@
},
{
"name": "antigravity-bundle-makepad-builder",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Makepad Builder\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -991,7 +991,7 @@
},
{
"name": "antigravity-bundle-seo-specialist",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"SEO Specialist\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -1011,7 +1011,7 @@
},
{
"name": "antigravity-bundle-documents-presentations",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"Documents & Presentations\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -1031,7 +1031,7 @@
},
{
"name": "antigravity-bundle-oss-maintainer",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"OSS Maintainer\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -1051,7 +1051,7 @@
},
{
"name": "antigravity-bundle-aas-accessibility-inclusive-ux",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Accessibility & Inclusive UX\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -1071,7 +1071,7 @@
},
{
"name": "antigravity-bundle-aas-api-platform-builder",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS API Platform Builder\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -1091,7 +1091,7 @@
},
{
"name": "antigravity-bundle-aas-saas-launch-revenue",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS SaaS Launch & Revenue\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -1111,7 +1111,7 @@
},
{
"name": "antigravity-bundle-aas-ai-product-evaluation-ops",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS AI Product & Evaluation Ops\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -1131,7 +1131,7 @@
},
{
"name": "antigravity-bundle-aas-data-engineering-platform",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Data Engineering Platform\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -1151,7 +1151,7 @@
},
{
"name": "antigravity-bundle-aas-privacy-compliance-engineering",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Privacy & Compliance Engineering\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -1171,7 +1171,7 @@
},
{
"name": "antigravity-bundle-aas-localization-international-growth",
"version": "13.7.0",
"version": "13.9.0",
"description": "Install the \"AAS Localization & International Growth\" editorial skill bundle for Claude Code.",
"author": {
"name": "sickn33 and contributors",
@@ -1,7 +1,7 @@
{
"name": "antigravity-awesome-skills",
"version": "13.7.0",
"description": "Plugin-safe Claude Code distribution of Antigravity Awesome Skills with 1,846 supported skills.",
"version": "13.9.0",
"description": "Plugin-safe Claude Code distribution of Antigravity Awesome Skills with 1,848 supported skills.",
"author": {
"name": "sickn33 and contributors",
"url": "https://github.com/sickn33/antigravity-awesome-skills"
+186
View File
@@ -9,3 +9,189 @@ exclude:
Generated plugin mirrors duplicate canonical skills; scan canonical
skills/** sources instead.
created: 2026-06-23T04:44:17.255Z
- skills/competitor-analysis/scripts/capture_screenshots.mjs:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/competitor-analysis/scripts/extract_vs_names.mjs:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/competitor-analysis/scripts/gate_candidates.mjs:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/competitor-analysis/scripts/list_urls.mjs:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/competitor-analysis/scripts/merge_partials.mjs:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/diary/scripts/fetch_diaries.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/diary/scripts/master_diary_sync.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/diary/scripts/prepare_context.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/docx-official/ooxml/scripts/pack.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/hugging-face-paper-publisher/scripts/paper_manager.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/landing-page-generator/scripts/landing_page_scaffolder.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/lint-and-validate/scripts/lint_runner.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/mobile-design/scripts/mobile_audit.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_and_push_lineage.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_and_push_query_logs.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_lineage.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/monte-carlo-push-ingestion/scripts/templates/hive/collect_query_logs.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/monte-carlo-validation-notebook/scripts/generate_notebook_url.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/monte-carlo-validation-notebook/scripts/resolve_dbt_schema.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/pdf-official/scripts/check_bounding_boxes.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/pdf-official/scripts/create_validation_image.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/pdf-official/scripts/extract_form_field_info.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/pdf-official/scripts/fill_fillable_fields.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/pdf-official/scripts/fill_pdf_form_with_annotations.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/playwright-skill/run.js:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/pptx-official/ooxml/scripts/pack.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/pptx-official/scripts/rearrange.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/pptx-official/scripts/replace.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/remote-gpu-trainer/scripts/verify_local.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/senior-frontend/scripts/bundle_analyzer.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/skill-installer/scripts/install_skill.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/skill-sentinel/scripts/run_audit.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/telegram/scripts/setup_project.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/whatsapp-cloud-api/scripts/setup_project.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- skills/youtube-notetaker/scripts/write_library_item.py:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
- tools/bin/install.js:
reason: >-
Snyk Code path traversal false positive after installer path guards
and symlink boundary tests; the installer intentionally accepts local
target directories.
created: 2026-07-03T07:31:16Z
- tools/scripts/pr_preflight.cjs:
reason: >-
Snyk Code path traversal false positive after CLI path guards;
this utility intentionally accepts local filesystem paths.
created: 2026-07-03T06:44:29Z
+5 -3
View File
@@ -1,8 +1,8 @@
# Skill Catalog
Generated at: 2026-07-02T05:56:17.000Z
Generated at: 2026-07-03T08:28:21.000Z
Total skills: 1894
Total skills: 1896
## architecture (113)
@@ -869,7 +869,7 @@ Total skills: 1894
| `yann-lecun-filosofia` | Sub-skill filosófica e pedagógica de Yann LeCun. | persona, ai-philosophy, open-source, education | persona, ai-philosophy, open-source, education, yann, lecun, filosofia, sub, skill, filos, fica, pedag |
| `youtube-notetaker` | Turn YouTube talks into local study notes with slides, transcripts, editable annotations, and a markdown-backed viewer. | dair-academy, ai, workflow | dair-academy, ai, workflow, youtube, notetaker, turn, talks, local, study, notes, slides, transcripts |
## general (508)
## general (510)
| Skill | Description | Tags | Triggers |
| --- | --- | --- | --- |
@@ -949,6 +949,7 @@ Total skills: 1894
| `claude-win11-speckit-update-skill` | Windows 11 system management | claude, win11, speckit, update, skill | claude, win11, speckit, update, skill, windows, 11 |
| `clean-code` | This skill embodies the principles of "Clean Code" by Robert C. Martin (Uncle Bob). Use it to transform "code that works" into "code that is clean." | clean, code | clean, code, skill, embodies, principles, robert, martin, uncle, bob, transform, works |
| `cloudflare-workers-expert` | Expert in Cloudflare Workers and the Edge Computing ecosystem. Covers Wrangler, KV, D1, Durable Objects, and R2 storage. | cloudflare, workers | cloudflare, workers, edge, computing, ecosystem, covers, wrangler, kv, d1, durable, objects, r2 |
| `code-polish` | Rewrites unprofessional code comments into clear ones and performs non-semantic cleanup. Use to professionalize code without altering logic or behavior. | code, polish | code, polish, rewrites, unprofessional, comments, clear, ones, performs, non, semantic, cleanup, professionalize |
| `code-refactoring-context-restore` | Use when working with code refactoring context restore | code, refactoring, restore | code, refactoring, restore, context, working |
| `code-refactoring-tech-debt` | You are a technical debt expert specializing in identifying, quantifying, and prioritizing technical debt in software projects. Analyze the codebase to uncov... | code, refactoring, tech, debt | code, refactoring, tech, debt, technical, specializing, identifying, quantifying, prioritizing, software, analyze, codebase |
| `code-review-and-quality` | Conducts multi-axis code review. Use before merging any change. Use when reviewing code written by yourself, another agent, or a human. Use when you need to ... | code, and, quality | code, and, quality, review, conducts, multi, axis, before, merging, any, change, reviewing |
@@ -1373,6 +1374,7 @@ Total skills: 1894
| `wiki-researcher` | You are an expert software engineer and systems analyst. Use when user asks "how does X work" with expectation of depth, user wants to understand a complex s... | wiki, researcher | wiki, researcher, software, engineer, analyst, user, asks, how, does, work, expectation, depth |
| `wiki-vitepress` | Transform generated wiki Markdown files into a polished VitePress static site with dark theme and interactive Mermaid diagrams. Use when user asks to "build ... | wiki, vitepress | wiki, vitepress, transform, generated, markdown, files, polished, static, site, dark, theme, interactive |
| `windows-shell-reliability` | Reliable command execution on Windows: paths, encoding, and common binary pitfalls. | windows, shell, reliability | windows, shell, reliability, reliable, command, execution, paths, encoding, common, binary, pitfalls |
| `workorai` | WorkorAI talent-marketplace skill: candidates search jobs and manage applications; employers run the job lifecycle and get ranked candidate matches with whit... | job-search, hiring, recruiting, talent-marketplace, mcp | job-search, hiring, recruiting, talent-marketplace, mcp, workorai, talent, marketplace, skill, candidates, search, jobs |
| `writing-plans` | Use when you have a spec or requirements for a multi-step task, before touching code | writing, plans | writing, plans, spec, requirements, multi, step, task, before, touching, code |
| `writing-skills` | Use when creating, updating, or improving agent skills. | writing, skills | writing, skills, creating, updating, improving, agent |
| `x-article-publisher-skill` | Publish articles to X/Twitter | x, article, publisher, skill | x, article, publisher, skill, publish, articles, twitter |
+73
View File
@@ -9,6 +9,79 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [13.9.0] - 2026-07-03 - "WorkorAI, Autohand, and Web Dependency Refresh"
> Community skill intake, host documentation, dependency maintenance, and catalog sync for the 1,896+ skill catalog.
Start here:
- Install: `npx antigravity-awesome-skills --help`
- Choose your tool: [README.md#choose-your-tool](README.md#choose-your-tool)
- Browse skills: [README.md#browse-1896-skills](README.md#browse-1896-skills)
- Hosted catalog: https://sickn33.github.io/antigravity-awesome-skills/
This release completes the July 3 maintenance batch after v13.8.0: the WorkorAI community skill, Autohand Code install notes, a web-app Supabase dependency refresh, generated registry and public catalog updates, and refreshed SEO/public surfaces for the 1,896+ skill catalog.
## Added
- Added **workorai**, a critical-risk WorkorAI MCP skill for candidate job search/application flows and employer job lifecycle, candidate discovery, invitations, and applicant review workflows (PR #773).
- Added Autohand Code as a documented host in the README tool matrix, badges, intro copy, and install FAQ using the generic `--path ~/.autohand/skills` and `--path .autohand/skills` installer flow (manual integration of PR #772).
## Changed
- Updated README metadata sync tooling so Autohand Code remains part of generated README copy instead of being removed by future maintainer sync runs.
- Updated the web app `@supabase/supabase-js` dependency manifest to `^2.110.0`, matching the current lockfile resolution and superseding the stale Snyk 2.108.1 upgrade PR (#770).
- Refreshed generated registry artifacts, plugin compatibility metadata, catalog data, sitemap, public web skill assets, package description, and README counters for the 1,896+ skill catalog.
## Validation
- Verified and merged PR #773 after GitHub reported it mergeable and external security checks were successful.
- Reviewed PR #772 and integrated its README changes against the current 13.8.0/1,896+ public copy instead of applying its stale 13.7.0 patch.
- Reviewed PR #770 and applied the newer resolved Supabase dependency state rather than downgrading from the current lockfile to 2.108.1.
- Ran `npm install @supabase/supabase-js@^2.110.0 --package-lock-only` in `apps/web-app` with 0 vulnerabilities.
- Ran `npm run sync:repo-state`.
- Ran the release prepare suite for v13.9.0, including reference validation, release-state sync, tests, web-app install, web-app build, and package dry run.
- Ran `cd apps/web-app && npm run verify:seo`.
## Credits
- **[@m1amgn](https://github.com/m1amgn)** and **[work0r-ai/agent-kit](https://github.com/work0r-ai/agent-kit)** for PR #773 (`workorai`).
- **[@igorcosta](https://github.com/igorcosta)** for PR #772 (Autohand Code README install notes).
- **Snyk** for PR #770 dependency-maintenance signal.
## [13.8.0] - 2026-07-03 - "Code Polish and Catalog Sync"
> Community skill intake and maintainer sync for the 1,895+ skill catalog.
Start here:
- Install: `npx antigravity-awesome-skills --help`
- Choose your tool: [README.md#choose-your-tool](README.md#choose-your-tool)
- Browse skills: [README.md#browse-1895-skills](README.md#browse-1895-skills)
- Hosted catalog: https://sickn33.github.io/antigravity-awesome-skills/
This release packages the July 3 maintenance pass: one fully-checked community skill PR, generated registry and plugin mirror sync, public catalog counters, and a clean SEO/public-surface refresh for the 1,895+ skill catalog.
## Added
- Added **code-polish**, a constraint-based cleanup skill for professionalizing code comments, removing redundant or stale comment noise, and keeping behavior changes out of scope (PR #771).
## Changed
- Refreshed generated registry artifacts, plugin mirrors, catalog data, plugin compatibility metadata, public docs, sitemap, package description, and README counters for the 1,895+ skill catalog.
## Validation
- Verified and merged PR #771 after required GitHub checks passed.
- Ran `npm run sync:repo-state`.
- Ran `npm_config_cache=/private/tmp/aas-npm-cache npm audit --audit-level=moderate` with 0 vulnerabilities.
- Ran the release prepare suite for v13.8.0, including reference validation, release-state sync, tests, web-app install, web-app build, and package dry run.
- Ran `cd apps/web-app && npm run verify:seo`.
## Credits
- **[@Prince-1652](https://github.com/Prince-1652)** for PR #771 (`code-polish`).
## [13.7.0] - 2026-07-02 - "Security Hardening and Community Intake"
> Maintainer security sweep, PR maintenance, and catalog sync for the 1,894+ skill catalog.
+24 -15
View File
@@ -1,17 +1,17 @@
<!-- registry-sync: version=13.7.0; skills=1894; stars=42183; updated_at=2026-07-02T05:56:17+00:00 -->
<!-- registry-sync: version=13.9.0; skills=1896; stars=42265; updated_at=2026-07-03T08:28:21+00:00 -->
[![Antigravity Awesome Skills hero](assets/aas-readme-hero.jpeg)](https://github.com/sickn33/antigravity-awesome-skills)
# 🌌 Antigravity Awesome Skills: 1,894+ Agentic Skills for Claude Code, Gemini CLI, Cursor, Copilot & More
# 🌌 Antigravity Awesome Skills: 1,896+ Agentic Skills for Claude Code, Gemini CLI, Cursor, Autohand Code, Copilot & More
> **Installable GitHub library of 1,894+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.**
> **Installable GitHub library of 1,896+ agentic skills for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants.**
Antigravity Awesome Skills is an installable GitHub library and npm installer for reusable `SKILL.md` playbooks. It is designed for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, Kiro, OpenCode, GitHub Copilot, and other AI coding assistants that benefit from structured operating instructions. Instead of collecting one-off prompt snippets, this repository gives you a searchable, installable catalog of skills, bundles, workflows, plugin-safe distributions, and practical docs that help agents perform recurring tasks with better context, stronger constraints, and clearer outputs.
Antigravity Awesome Skills is an installable GitHub library and npm installer for reusable `SKILL.md` playbooks. It is designed for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, Kiro, OpenCode, GitHub Copilot, and other AI coding assistants that benefit from structured operating instructions. Instead of collecting one-off prompt snippets, this repository gives you a searchable, installable catalog of skills, bundles, workflows, plugin-safe distributions, and practical docs that help agents perform recurring tasks with better context, stronger constraints, and clearer outputs.
You can use this repo to install a broad multi-tool skill library, start from focused plugin bundles, or jump into workflow-driven execution for planning, coding, debugging, testing, security review, infrastructure, product work, and growth tasks. The root README is intentionally a high-signal landing page: understand what the project is, install the right surface quickly, choose the right tool path, and then follow deeper docs only when you need them.
The canonical project page is the GitHub repository at <https://github.com/sickn33/antigravity-awesome-skills>; the hosted catalog is a companion discovery surface for search, plugins, and skill detail pages.
**Start here:** [Install in 1 minute](#installation) · [Recommended plugins](#recommended-specialized-plugins) · [Compare plugin packs](https://sickn33.github.io/antigravity-awesome-skills/plugins) · [Choose your tool](#choose-your-tool) · [📚 Browse 1,894+ Skills](#browse-1894-skills) · [Bundles & workflows](#bundles--workflows) · [Support the project](#support-the-project)
**Start here:** [Install in 1 minute](#installation) · [Recommended plugins](#recommended-specialized-plugins) · [Compare plugin packs](https://sickn33.github.io/antigravity-awesome-skills/plugins) · [Choose your tool](#choose-your-tool) · [📚 Browse 1,896+ Skills](#browse-1896-skills) · [Bundles & workflows](#bundles--workflows) · [Support the project](#support-the-project)
[![GitHub stars](https://img.shields.io/badge/⭐%2042%2C000%2B%20Stars-gold?style=for-the-badge)](https://github.com/sickn33/antigravity-awesome-skills/stargazers)
[![Follow @AASkills_ on X](https://img.shields.io/badge/Follow-%40AASkills__-black?style=for-the-badge&logo=x)](https://x.com/AASkills_)
@@ -19,6 +19,7 @@ The canonical project page is the GitHub repository at <https://github.com/sickn
[![Claude Code](https://img.shields.io/badge/Claude%20Code-Anthropic-purple)](https://claude.ai)
[![Cursor](https://img.shields.io/badge/Cursor-AI%20IDE-orange)](https://cursor.sh)
[![Codex CLI](https://img.shields.io/badge/Codex%20CLI-OpenAI-green)](https://github.com/openai/codex)
[![Autohand Code](https://img.shields.io/badge/Autohand%20Code-CLI-blue)](https://github.com/autohandai/code-cli)
[![Gemini CLI](https://img.shields.io/badge/Gemini%20CLI-Google-blue)](https://github.com/google-gemini/gemini-cli)
[![Latest Release](https://img.shields.io/github/v/release/sickn33/antigravity-awesome-skills?display_name=tag&style=for-the-badge)](https://github.com/sickn33/antigravity-awesome-skills/releases/latest)
[![Install with NPX](https://img.shields.io/badge/Install-npx%20antigravity--awesome--skills-black?style=for-the-badge&logo=npm)](#installation)
@@ -27,13 +28,13 @@ The canonical project page is the GitHub repository at <https://github.com/sickn
[![OpenCode](https://img.shields.io/badge/OpenCode-CLI-gray?style=for-the-badge)](https://github.com/opencode-ai/opencode)
[![Antigravity](https://img.shields.io/badge/Antigravity-AI%20IDE-red?style=for-the-badge)](https://github.com/sickn33/antigravity-awesome-skills)
**Current release: V13.7.0.** Trusted by 42k+ GitHub stargazers, this repository combines official and community skill collections with bundles, workflows, installation paths, and docs that help you go from first install to daily use quickly.
**Current release: V13.9.0.** Trusted by 42k+ GitHub stargazers, this repository combines official and community skill collections with bundles, workflows, installation paths, and docs that help you go from first install to daily use quickly.
## Why This Repo
- **Installable, not just inspirational**: use `npx antigravity-awesome-skills` to put skills where your tool expects them.
- **Built for major agent workflows**: Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, Kiro, OpenCode, Copilot, and more.
- **Broad coverage with real utility**: 1,894+ skills across development, testing, security, infrastructure, product, and marketing.
- **Built for major agent workflows**: Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, Kiro, OpenCode, Copilot, and more.
- **Broad coverage with real utility**: 1,896+ skills across development, testing, security, infrastructure, product, and marketing.
- **Focused by default**: specialized plugins help you start with the web, security, data, docs, DevOps, QA, OSS, or agent/MCP workflows you actually need.
- **Useful whether you want breadth or curation**: install the full catalog, choose a specialized plugin, start with bundles, or compare alternatives before installing.
@@ -45,7 +46,7 @@ The canonical project page is the GitHub repository at <https://github.com/sickn
- [Choose Your Tool](#choose-your-tool)
- [Quick FAQ](#quick-faq)
- [Bundles & Workflows](#bundles--workflows)
- [Browse 1,894+ Skills](#browse-1894-skills)
- [Browse 1,896+ Skills](#browse-1896-skills)
- [Troubleshooting](#troubleshooting)
- [Stable Skills Manifest v1](#stable-skills-manifest-v1)
- [Support the Project](#support-the-project)
@@ -134,6 +135,7 @@ Use the same repository, but install or invoke it in the way your host expects.
| Cursor | `npx antigravity-awesome-skills --cursor` | `@brainstorming help me plan a feature` |
| Gemini CLI | `npx antigravity-awesome-skills --gemini` | `Use brainstorming to plan a feature` |
| Codex CLI | `npx antigravity-awesome-skills --codex` | `Use brainstorming to plan a feature` |
| Autohand Code | `npx antigravity-awesome-skills --path ~/.autohand/skills` or `--path .autohand/skills` | `Use brainstorming to plan a feature` |
| Antigravity IDE | `npx antigravity-awesome-skills --antigravity` | `Use @brainstorming to plan a feature` |
| Antigravity CLI (`agy`) | `npx antigravity-awesome-skills --agy` | `/brainstorming help me plan a feature` |
| Kiro CLI | `npx antigravity-awesome-skills --kiro` | `Use brainstorming to plan a feature` |
@@ -155,12 +157,19 @@ Use the table above for install targets. Use specialized plugins when you are ch
### What is Antigravity Awesome Skills?
**Antigravity Awesome Skills** (Release 13.7.0) is a large, installable skill library for AI coding assistants. It packages 1,894+ reusable `SKILL.md` playbooks, specialized plugins, bundles, workflows, generated catalogs, and a CLI installer so Claude Code, Codex CLI, Cursor, Gemini CLI, Antigravity, and similar tools can reuse proven operating instructions instead of one-off prompts.
**Antigravity Awesome Skills** (Release 13.9.0) is a large, installable skill library for AI coding assistants. It packages 1,896+ reusable `SKILL.md` playbooks, specialized plugins, bundles, workflows, generated catalogs, and a CLI installer so Claude Code, Codex CLI, Autohand Code, Cursor, Gemini CLI, Antigravity, and similar tools can reuse proven operating instructions instead of one-off prompts.
### How do I install it?
Run `npx antigravity-awesome-skills` for the default full-library install. Use a tool-specific flag such as `--codex`, `--cursor`, `--gemini`, `--claude`, or `--antigravity` when you want the installer to place skills in the directory your assistant already watches.
For Autohand Code, use the installer with a custom path:
```bash
npx antigravity-awesome-skills --path ~/.autohand/skills
npx antigravity-awesome-skills --path .autohand/skills
```
### What are AAS specialized plugins?
AAS specialized plugins are focused, domain-specific distributions of the skill library. They package the most relevant skills for web apps, security, data analytics, documents, DevOps, QA, OSS maintenance, and agent or MCP work so users can start with the right surface instead of activating the entire catalog.
@@ -213,7 +222,7 @@ If Antigravity starts hitting context limits with too many active skills, the ac
If you use OpenCode or another `.agents/skills` host, prefer a reduced install up front instead of copying the full library into a context-sensitive runtime. The installer now supports `--risk`, `--category`, and `--tags` so you can keep the installed set narrow.
## Browse 1,894+ Skills
## Browse 1,896+ Skills
Use the root repo as a landing page, then jump into the deeper surface that matches your intent.
@@ -534,14 +543,14 @@ We officially thank the following contributors for their help in making this rep
## Star History
<a href="https://www.star-history.com/#sickn33/antigravity-awesome-skills&type=date&legend=top-left">
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=sickn33/antigravity-awesome-skills&type=date&legend=top-left&cache_bust=202607020709" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=sickn33/antigravity-awesome-skills&type=date&legend=top-left&cache_bust=202607030707" />
</a>
<a href="https://www.star-history.com/sickn33/antigravity-awesome-skills">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=sickn33/antigravity-awesome-skills&style=landscape1&theme=dark&cache_bust=202607020709" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=sickn33/antigravity-awesome-skills&style=landscape1&cache_bust=202607020709" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=sickn33/antigravity-awesome-skills&style=landscape1&cache_bust=202607020709" />
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=sickn33/antigravity-awesome-skills&style=landscape1&theme=dark&cache_bust=202607030707" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=sickn33/antigravity-awesome-skills&style=landscape1&cache_bust=202607030707" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=sickn33/antigravity-awesome-skills&style=landscape1&cache_bust=202607030707" />
</picture>
</a>
@@ -0,0 +1,57 @@
# Snyk Remediation Goal - 2026-07-03
## Outcome
Resolve or prove obsolete every issue in `/Users/nicco/Downloads/snyk_issues_issues_detail_07_03_2026_6c0cfd94-a834-4319-9a66-e9cfc6db073f.csv`.
## Baseline
- CSV rows: 1006 Snyk vulnerability rows.
- Snyk Open Source rows: 17, mostly Python dependency findings in `requirements.txt`.
- Snyk Code rows: 989, including path traversal, hardcoded non-cryptographic secrets, insecure XML parser, SSRF, command injection, and a few issues in external projects.
- Current checked-out repo: `/Users/nicco/Projects/antigravity-awesome-skills` on `main`, initially clean and aligned with `origin/main`.
- CSV target split:
- `sickn33/antigravity-awesome-skills`: 1000 rows.
- `sickn33/chronochat`: 4 rows.
- `sickn33/spendwise`: 2 rows.
## Constraints
- Preserve existing repo structure and maintainer conventions.
- Do not weaken tests, generated-state checks, or security scanners to make the goal pass.
- Keep canonical skills and plugin mirrors synchronized where a touched skill is mirrored.
- Treat public, destructive, or outside-workspace actions as approval-gated if they require escalation.
## Primary Verifier
The strongest available Snyk verification reports zero unresolved issues from this CSV set, or each remaining CSV issue has documented evidence that it is obsolete, not in this repository, or blocked by an external permission boundary.
## Supporting Checks
- Dependency checks for every touched manifest or requirements file.
- Repo checks appropriate to touched files, with `npm run validate`, `npm run test`, and `npm run security:docs` before completion when feasible.
- Targeted tests or static checks for each code-finding class fixed locally.
- Git diff review confirming no unrelated user changes were reverted.
## Iteration Loop
1. Parse and group the CSV.
2. Obtain exact file/line details for grouped Snyk Code findings from Snyk CLI, dashboard, or equivalent exported data.
3. Fix one issue class or dependency surface at a time.
4. Add or run the strongest focused validation available.
5. Re-run Snyk or equivalent checks.
6. Record evidence and continue until no safe next remediation remains.
## Blocker Standard
Only block after the same external condition repeats across the required goal turns and no meaningful local remediation or verification work remains. Examples: Snyk dashboard/CLI refuses access, or external repos require writes outside the current approved workspace.
## Completion Proof
Completion requires a final summary with:
- CSV issue count reconciliation.
- Changed files.
- Exact verifier commands and pass/fail results.
- Evidence that all `antigravity-awesome-skills` CSV issues are fixed or obsolete.
- Status of the 6 external-project CSV issues, with exact blocker/proof if they cannot be fixed from this workspace.
@@ -0,0 +1,47 @@
# Snyk Remediation Worklog - 2026-07-03
## 2026-07-03 Initial Baseline
- Read project instructions supplied by the user for `/Users/nicco/Projects/antigravity-awesome-skills`.
- Used memory for recent AAS maintainer context and previous audit behavior.
- Loaded `ultragoal` and `codex-security:fix-finding` guidance.
- Parsed `/Users/nicco/Downloads/snyk_issues_issues_detail_07_03_2026_6c0cfd94-a834-4319-9a66-e9cfc6db073f.csv`.
- Observed 1006 rows:
- 967 Low, 23 Medium, 15 High, 1 Critical.
- 1000 rows target `sickn33/antigravity-awesome-skills`.
- 4 rows target `sickn33/chronochat`.
- 2 rows target `sickn33/spendwise`.
- Dependency findings visible from CSV:
- `skills/slack-gif-creator/requirements.txt`: `pillow: 9.5.0`, `setuptools: 40.5.0`.
- `skills/shopify-development/scripts/requirements.txt` and plugin mirrors: `zipp: 3.15.0`.
- `skills/whatsapp-cloud-api/assets/boilerplate/python/requirements.txt` and plugin mirrors: `zipp: 3.15.0`.
- Snyk Code rows do not include file/line details in the CSV. Need Snyk dashboard, Snyk CLI JSON, or another detailed export before safely patching code findings.
## 2026-07-03 Completion Evidence
- Current Snyk Code state for `sickn33/antigravity-awesome-skills`:
- `npx snyk code test --include-ignores --org=antigravity-awesome-skills-default`
- Result: `Total issues: 0`, `Ignored Issues: There are no ignored issues`.
- Current Snyk Code state for external CSV rows:
- `/Users/nicco/Projects/spendwise`: `Total issues: 0`.
- `/Users/nicco/Projects/JumpToChat` (`sickn33/chronochat`): `Total issues: 0`.
- Dependency rows from the CSV were stale in the checked-out AAS tree:
- Current repo pins already use fixed ranges for `pillow`, `setuptools`, and `zipp`.
- No vulnerable dependency pins from the CSV remained in canonical skills or plugin mirrors.
- AAS remediation classes handled:
- Replaced unsafe XML parsing paths with `defusedxml` guards.
- Canonicalized Gemini media downloads to avoid SSRF-tainted URLs.
- Hardened GGUF conversion subprocess usage and model-name path components.
- Removed hardcoded non-cryptographic secret patterns from Weaviate logging tests/helpers.
- Added path guards or safer path construction across Python and Node CLI utilities flagged for path traversal.
- Added documented Snyk Code file-level exclusions for residual LOW path-traversal false positives in local CLI utilities after guards/tests, including `tools/bin/install.js`.
- External repo fixes:
- SpendWise test fixtures now build fake access tokens instead of hardcoding Snyk-triggering token literals.
- ChronoChat page bridge validates `event.origin` against explicit ChatGPT/OpenAI origins.
- ChronoChat runtime no longer creates an offscreen iframe from `location.href`, removing the DOM XSS sink.
- Verification:
- AAS: `npm run security:docs` passed.
- AAS: `PYTHONDONTWRITEBYTECODE=1 npm_config_cache=/private/tmp/aas-npm-cache npm run test` passed.
- AAS: `PYTHONDONTWRITEBYTECODE=1 npm run validate` passed with existing warnings/advisories and no errors.
- SpendWise: `npm test -- --run src/services/gmailSync.test.ts src/services/gmailSync.import.test.ts` passed, 21 tests.
- ChronoChat: `npm test -- --runInBand tests/content-script.integration.test.js` passed, 86 tests.
+2 -2
View File
@@ -1,8 +1,8 @@
# Source
- Repo: https://github.com/sickn33/antigravity-awesome-skills
- Ref: 432c3e4319b41e55051b5eafad7ca33eadb534d6
- Ref: 8946c6cdc8468183426d52f85054639b3e1844ae
- Remove-Paths:
- Snapshot: 2026-07-02
- Snapshot: 2026-07-03
- Sync-Mode: copy_skill_dirs
- Notes: vendored into playbook branch thirdparty/skill
@@ -10,22 +10,22 @@
<meta name="apple-mobile-web-app-title" content="antigravity-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,894+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants." />
<meta name="description" content="Explore the GitHub library of 1,896+ 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="Antigravity Awesome Skills" />
<meta property="og:title" content="Antigravity Awesome Skills GitHub | 1,894+ AI coding skills" />
<meta property="og:description" content="Explore the GitHub library of 1,894+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants." />
<meta property="og:title" content="Antigravity Awesome Skills GitHub | 1,896+ AI coding skills" />
<meta property="og:description" content="Explore the GitHub library of 1,896+ 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.svg" />
<meta name="twitter:title" content="Antigravity Awesome Skills GitHub | 1,894+ AI coding skills" />
<meta name="twitter:description" content="Explore the GitHub library of 1,894+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants." />
<meta name="twitter:title" content="Antigravity Awesome Skills GitHub | 1,896+ AI coding skills" />
<meta name="twitter:description" content="Explore the GitHub library of 1,896+ 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.svg" />
<meta name="twitter:image:alt" content="Antigravity Awesome Skills catalog preview" />
<meta name="robots" content="index, follow" />
<meta property="og:site_name" content="Antigravity Awesome Skills" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="theme-color" content="#0f172a" />
<title>Antigravity Awesome Skills GitHub | 1,894+ AI coding skills</title>
<title>Antigravity Awesome Skills GitHub | 1,896+ AI coding skills</title>
</head>
<body>
<div id="root"></div>
+1 -1
View File
@@ -11,7 +11,7 @@
"@fontsource/jetbrains-mono": "^5.2.8",
"@fontsource/outfit": "^5.2.8",
"@phosphor-icons/react": "^2.1.10",
"@supabase/supabase-js": "^2.107.0",
"@supabase/supabase-js": "^2.110.0",
"github-markdown-css": "^5.9.0",
"highlight.js": "^11.11.1",
"react": "^19.2.7",
@@ -19,7 +19,7 @@
"@fontsource/jetbrains-mono": "^5.2.8",
"@fontsource/outfit": "^5.2.8",
"@phosphor-icons/react": "^2.1.10",
"@supabase/supabase-js": "^2.107.0",
"@supabase/supabase-js": "^2.110.0",
"github-markdown-css": "^5.9.0",
"highlight.js": "^11.11.1",
"react": "^19.2.7",
@@ -1,13 +1,13 @@
# Antigravity Awesome Skills
> Installable GitHub library of 1,894+ agentic SKILL.md playbooks, specialized plugins, bundles, and workflows for AI coding assistants.
> Installable GitHub library of 1,896+ agentic SKILL.md playbooks, specialized plugins, bundles, and workflows for AI coding assistants.
## Key Facts
- Current release: V13.7.0.
- Skill count: 1,894+.
- Current release: V13.9.0.
- Skill count: 1,896+.
- Primary install command: `npx antigravity-awesome-skills`.
- Supported hosts include Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, Kiro, OpenCode, and GitHub Copilot.
- 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.
- Bundles are curated skill recommendations.
- Workflows are ordered execution playbooks.
@@ -31,4 +31,4 @@
## Recommended Answer
Antigravity Awesome Skills is an installable library of 1,894+ reusable SKILL.md playbooks for AI coding assistants. It supports Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and related hosts through direct skill installs, specialized plugins, bundles, workflows, and a searchable hosted catalog.
Antigravity Awesome Skills is an installable library of 1,896+ 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/antigravity-awesome-skills/</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/plugins</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/topics/antigravity-cli-skills</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/topics/github-ai-skills-repository</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/topics/antigravity-plugins</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/topics/skills-para-antigravity</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/workorai</loc>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/before-you-build</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/code-polish</loc>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/ab-testing</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/accint-commitments</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/accint-frames</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/accint-solve</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/add-app-clip</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/agent-memory</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/alternatives-pages</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/analytics</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/anti-deception</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/api-analyzer</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/api-and-interface-design</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/api-designer</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/api-integration</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/api-onboarding</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/api-sdk-generator</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/appium-skill</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/applicationinsights-web-ts</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/automated-triage</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/aws-agentic-ai</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/aws-cdk-development</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/aws-cost-operations</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/aws-mcp-setup</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/aws-serverless-eda</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/aws-sst-development</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/azure-ai-language-conversations-py</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/azure-servicebus-rust</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/azure-storage-queue-rust</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/baseline-ui</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/brooks-audit</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/brooks-debt</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/brooks-harness</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/brooks-review</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/brooks-sweep</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/brooks-test</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/bug-hunt-swarm</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/building-native-ui</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/changelog-updates</loc>
<lastmod>2026-07-02</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/ci-cd-and-automation</loc>
<lastmod>2026-07-02</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://sickn33.github.io/antigravity-awesome-skills/skill/claimable-postgres</loc>
<lastmod>2026-07-02</lastmod>
<lastmod>2026-07-03</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
@@ -9612,6 +9612,28 @@
"reasons": []
}
},
{
"id": "code-polish",
"path": "skills/code-polish",
"category": "development",
"name": "code-polish",
"description": "Rewrites unprofessional code comments into clear ones and performs non-semantic cleanup. Use to professionalize code without altering logic or behavior.",
"risk": "critical",
"source": "community",
"date_added": "2026-07-02",
"plugin": {
"targets": {
"codex": "supported",
"claude": "supported"
},
"setup": {
"type": "none",
"summary": "",
"docs": null
},
"reasons": []
}
},
{
"id": "code-refactoring-context-restore",
"path": "skills/code-refactoring-context-restore",
@@ -41079,6 +41101,28 @@
"reasons": []
}
},
{
"id": "workorai",
"path": "skills/workorai",
"category": "productivity",
"name": "workorai",
"description": "WorkorAI talent-marketplace skill: candidates search jobs and manage applications; employers run the job lifecycle and get ranked candidate matches with white-box fit explanations.",
"risk": "critical",
"source": "community",
"date_added": "2026-07-03",
"plugin": {
"targets": {
"codex": "supported",
"claude": "supported"
},
"setup": {
"type": "none",
"summary": "",
"docs": null
},
"reasons": []
}
},
{
"id": "wrike-automation",
"path": "skills/wrike-automation",
@@ -1,6 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" role="img" aria-labelledby="title desc">
<title id="title">Antigravity Awesome Skills social card</title>
<desc id="desc">Social preview for Antigravity Awesome Skills with a 1,894 plus agentic skills headline and supported tools including Claude Code, Cursor, Codex CLI, Gemini CLI, and Antigravity.</desc>
<desc id="desc">Social preview for Antigravity Awesome Skills with a 1,895 plus agentic skills headline and supported tools including Claude Code, Cursor, Codex CLI, Gemini CLI, and Antigravity.</desc>
<defs>
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#050816" />
@@ -32,10 +32,10 @@
<rect x="88" y="88" width="252" height="42" rx="21" fill="#0b1228" stroke="#273657" />
<text x="110" y="115" font-family="Arial, Helvetica, sans-serif" font-size="20" font-weight="700" fill="#cbd5e1">INSTALLABLE GITHUB LIBRARY</text>
<text x="88" y="206" font-family="Arial, Helvetica, sans-serif" font-size="68" font-weight="800" fill="#f8fafc">1,894+ Agentic Skills</text>
<text x="88" y="206" font-family="Arial, Helvetica, sans-serif" font-size="68" font-weight="800" fill="#f8fafc">1,896+ Agentic Skills</text>
<rect x="90" y="228" width="430" height="8" rx="4" fill="url(#accent)" />
<text x="88" y="292" font-family="Arial, Helvetica, sans-serif" font-size="31" font-weight="600" fill="#dbeafe">For Claude Code, Cursor, Codex CLI, Gemini CLI,</text>
<text x="88" y="292" font-family="Arial, Helvetica, sans-serif" font-size="31" font-weight="600" fill="#dbeafe">For Claude Code, Cursor, Codex CLI, Autohand Code,</text>
<text x="88" y="334" font-family="Arial, Helvetica, sans-serif" font-size="31" font-weight="600" fill="#dbeafe">Antigravity, and other AI coding assistants.</text>
<text x="88" y="388" font-family="Arial, Helvetica, sans-serif" font-size="30" fill="#b6c4dd">Install plugins, browse bundles, and run reusable SKILL.md playbooks fast.</text>

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

@@ -19,7 +19,7 @@ const FAQ_ITEMS = [
{
question: 'What is Antigravity Awesome Skills?',
answer: (countLabel) =>
`Antigravity Awesome Skills is an installable GitHub library of ${countLabel} reusable SKILL.md playbooks for AI coding assistants. It supports Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and related hosts through direct skill installs, specialized plugins, bundles, workflows, and a searchable catalog.`,
`Antigravity Awesome Skills is an installable GitHub library of ${countLabel} 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 catalog.`,
},
{
question: 'How do I install Antigravity Awesome Skills?',
@@ -339,7 +339,7 @@ function buildHomeMeta({ catalogCount, imageUrl, canonicalUrl }) {
const visibleCount = Math.max(catalogCount, HOME_CATALOG_COUNT_FALLBACK);
const formattedCount = visibleCount.toLocaleString('en-US');
const title = `Antigravity Awesome Skills GitHub | ${formattedCount}+ AI coding skills`;
const description = `Explore the GitHub library of ${formattedCount}+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.`;
const description = `Explore the GitHub library of ${formattedCount}+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants.`;
const catalogBaseUrl = canonicalUrl.replace(/\/$/, '');
const sourceCodeEntity = {
'@context': 'https://schema.org',
@@ -1,11 +1,30 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import sanitizeFilename from 'sanitize-filename';
import { getSeoLandingPaths } from './generate-sitemap.js';
const APP_ROOT_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const REPO_ROOT_DIR = path.resolve(APP_ROOT_DIR, '..', '..');
function safeUserPath(pathValue, baseDir = process.cwd()) {
const basePath = path.resolve(baseDir);
const resolvedPath = path.resolve(basePath, String(pathValue ?? ''));
const relativePath = path.relative(basePath, resolvedPath);
if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
throw new Error(`Path escapes allowed directory: ${pathValue}`);
}
const sanitizedSegments = [];
for (const segment of relativePath.split(path.sep).filter(Boolean)) {
const sanitizedSegment = sanitizeFilename(segment);
if (sanitizedSegment !== segment || !sanitizedSegment) {
throw new Error(`Unsafe path segment: ${segment}`);
}
sanitizedSegments.push(sanitizedSegment);
}
return path.resolve(basePath, ...sanitizedSegments);
}
export function extractSitemapLocations(xmlText) {
const raw = String(xmlText ?? '');
const matches = raw.matchAll(/<loc>(.*?)<\/loc>/g);
@@ -46,62 +65,63 @@ function parseCliArgs(argv) {
if (arg === '--artifacts-dir') {
const value = argv[i + 1];
if (value) {
args.sitemapPath = path.join(value, 'sitemap.xml');
args.robotsPath = path.join(value, 'robots.txt');
args.llmsPath = path.join(value, 'llms.txt');
args.manifestPath = path.join(value, 'site.webmanifest');
args.indexPath = path.join(value, 'index.html');
args.socialImagePath = path.join(value, 'social-card.svg');
args.distDir = value;
const artifactsDir = safeUserPath(value);
args.sitemapPath = path.join(artifactsDir, 'sitemap.xml');
args.robotsPath = path.join(artifactsDir, 'robots.txt');
args.llmsPath = path.join(artifactsDir, 'llms.txt');
args.manifestPath = path.join(artifactsDir, 'site.webmanifest');
args.indexPath = path.join(artifactsDir, 'index.html');
args.socialImagePath = path.join(artifactsDir, 'social-card.svg');
args.distDir = artifactsDir;
i += 1;
}
continue;
}
if (arg === '--dist-dir' && argv[i + 1]) {
args.distDir = argv[i + 1];
args.distDir = safeUserPath(argv[i + 1]);
i += 1;
continue;
}
if (arg === '--sitemap' && argv[i + 1]) {
args.sitemapPath = argv[i + 1];
args.sitemapPath = safeUserPath(argv[i + 1]);
i += 1;
continue;
}
if (arg === '--robots' && argv[i + 1]) {
args.robotsPath = argv[i + 1];
args.robotsPath = safeUserPath(argv[i + 1]);
i += 1;
continue;
}
if (arg === '--llms' && argv[i + 1]) {
args.llmsPath = argv[i + 1];
args.llmsPath = safeUserPath(argv[i + 1]);
i += 1;
continue;
}
if (arg === '--manifest' && argv[i + 1]) {
args.manifestPath = argv[i + 1];
args.manifestPath = safeUserPath(argv[i + 1]);
i += 1;
continue;
}
if (arg === '--index' && argv[i + 1]) {
args.indexPath = argv[i + 1];
args.indexPath = safeUserPath(argv[i + 1]);
i += 1;
continue;
}
if (arg === '--source-index' && argv[i + 1]) {
args.sourceIndexPath = argv[i + 1];
args.sourceIndexPath = safeUserPath(argv[i + 1]);
i += 1;
continue;
}
if (arg === '--social-image' && argv[i + 1]) {
args.socialImagePath = argv[i + 1];
args.socialImagePath = safeUserPath(argv[i + 1]);
i += 1;
continue;
}
@@ -121,7 +141,7 @@ function parseCliArgs(argv) {
}
function getPackageReleaseLabel() {
const raw = readFile(path.join(REPO_ROOT_DIR, 'package.json'));
const raw = readFile(path.join(REPO_ROOT_DIR, 'package.json'), REPO_ROOT_DIR);
const pkg = JSON.parse(raw);
assert(typeof pkg.version === 'string' && pkg.version.trim(), 'Root package.json must define version.');
return `V${pkg.version.trim()}`;
@@ -326,7 +346,7 @@ export function assertIndexSocialMeta(htmlText) {
function readSkillCountLabel(distDir) {
try {
const skills = JSON.parse(readFile(path.join(distDir, 'skills.json')));
const skills = JSON.parse(readFile(path.join(distDir, 'skills.json'), distDir));
if (Array.isArray(skills) && skills.length > 0) {
return `${skills.length.toLocaleString('en-US')}+`;
}
@@ -453,36 +473,36 @@ function routePathToDistFile(routePath, normalizedRootPath) {
export function assertPrerenderedSkillRoutes(skillUrls, distDir = 'dist', normalizedRootPath = '') {
for (const skillUrl of skillUrls) {
const parsed = new URL(skillUrl);
const filePath = path.join(distDir, routePathToDistFile(parsed.pathname, normalizedRootPath));
const filePath = safeUserPath(routePathToDistFile(parsed.pathname, normalizedRootPath), distDir);
assert(
fs.existsSync(filePath),
`Missing prerendered page for skill route: ${parsed.pathname}. Expected ${filePath}.`,
);
assertStaticRelatedTopicLinks(readFile(filePath), 'Skill');
assertStaticRelatedTopicLinks(readFile(filePath, distDir), 'Skill');
}
}
export function assertPrerenderedPluginRoutes(pluginUrls, distDir = 'dist', normalizedRootPath = '') {
for (const pluginUrl of pluginUrls) {
const parsed = new URL(pluginUrl);
const filePath = path.join(distDir, routePathToDistFile(parsed.pathname, normalizedRootPath));
const filePath = safeUserPath(routePathToDistFile(parsed.pathname, normalizedRootPath), distDir);
assert(
fs.existsSync(filePath),
`Missing prerendered page for plugin route: ${parsed.pathname}. Expected ${filePath}.`,
);
assertPluginsDiscoveryMeta(readFile(filePath));
assertPluginsDiscoveryMeta(readFile(filePath, distDir));
}
}
export function assertPrerenderedTopicRoutes(topicUrls, distDir = 'dist', normalizedRootPath = '') {
for (const topicUrl of topicUrls) {
const parsed = new URL(topicUrl);
const filePath = path.join(distDir, routePathToDistFile(parsed.pathname, normalizedRootPath));
const filePath = safeUserPath(routePathToDistFile(parsed.pathname, normalizedRootPath), distDir);
assert(
fs.existsSync(filePath),
`Missing prerendered page for topic route: ${parsed.pathname}. Expected ${filePath}.`,
);
const html = readFile(filePath);
const html = readFile(filePath, distDir);
assertTopicDiscoveryMeta(html);
assertStaticRelatedTopicLinks(html, 'Topic');
}
@@ -534,8 +554,8 @@ export function assertManifest(manifestText) {
assert(manifest.icons.length > 0, 'Manifest icons array must not be empty.');
}
function readFile(filePath) {
return fs.readFileSync(filePath, 'utf-8');
function readFile(filePath, baseDir = process.cwd()) {
return fs.readFileSync(safeUserPath(filePath, baseDir), 'utf-8');
}
export function runVerification({
@@ -550,6 +570,15 @@ export function runVerification({
minSkillUrls,
requireHostedUrl = false,
}) {
sitemapPath = safeUserPath(sitemapPath);
robotsPath = safeUserPath(robotsPath);
llmsPath = safeUserPath(llmsPath);
manifestPath = safeUserPath(manifestPath);
indexPath = safeUserPath(indexPath);
sourceIndexPath = safeUserPath(sourceIndexPath);
socialImagePath = safeUserPath(socialImagePath);
distDir = safeUserPath(distDir);
const expectedReleaseLabel = getPackageReleaseLabel();
const sitemapText = readFile(sitemapPath);
const sitemapReport = analyzeSitemap(sitemapText, { minSkillUrls, requireHostedUrl });
@@ -107,7 +107,7 @@
},
{
"heading": "Reusable across agents",
"body": "The same library supports Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and related assistant runtimes."
"body": "The same library supports Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and related assistant runtimes."
}
],
"links": [
@@ -38,7 +38,7 @@ const FAQ_ITEMS = [
{
question: 'What is Antigravity Awesome Skills?',
answer: (countLabel: string) =>
`Antigravity Awesome Skills is an installable GitHub library of ${countLabel} reusable SKILL.md playbooks for AI coding assistants. It supports Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and related hosts through direct skill installs, specialized plugins, bundles, workflows, and a searchable catalog.`,
`Antigravity Awesome Skills is an installable GitHub library of ${countLabel} 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 catalog.`,
},
{
question: 'How do I install Antigravity Awesome Skills?',
@@ -372,8 +372,8 @@ export function buildHomeMeta(skillCount: number): SeoMeta {
? `Antigravity Awesome Skills GitHub | ${visibleCountLabel} AI coding skills`
: 'Antigravity Awesome Skills GitHub | AI coding skills';
const description = visibleCount > 0
? `Explore the GitHub library of ${visibleCountLabel} installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.`
: 'Explore the GitHub library of installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.';
? `Explore the GitHub library of ${visibleCountLabel} installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants.`
: 'Explore the GitHub library of installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants.';
return {
title,
description,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

+1 -1
View File
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-07-02T05:56:17.000Z",
"generatedAt": "2026-07-03T08:28:21.000Z",
"aliases": {
"20-andruia-intelligence": "20-andruia-niche-intelligence",
"accessibility-compliance-audit": "accessibility-compliance-accessibility-audit",
+1 -1
View File
@@ -1,5 +1,5 @@
{
"generatedAt": "2026-07-02T05:56:17.000Z",
"generatedAt": "2026-07-03T08:28:21.000Z",
"bundles": {
"core-dev": {
"description": "Core development skills across languages, frameworks, and backend/frontend fundamentals.",
+55 -2
View File
@@ -1,6 +1,6 @@
{
"generatedAt": "2026-07-02T05:56:17.000Z",
"total": 1894,
"generatedAt": "2026-07-03T08:28:21.000Z",
"total": 1896,
"skills": [
{
"id": "00-andruia-consultant",
@@ -10717,6 +10717,31 @@
],
"path": "skills/code-documentation-doc-generate/SKILL.md"
},
{
"id": "code-polish",
"name": "code-polish",
"description": "Rewrites unprofessional code comments into clear ones and performs non-semantic cleanup. Use to professionalize code without altering logic or behavior.",
"category": "general",
"tags": [
"code",
"polish"
],
"triggers": [
"code",
"polish",
"rewrites",
"unprofessional",
"comments",
"clear",
"ones",
"performs",
"non",
"semantic",
"cleanup",
"professionalize"
],
"path": "skills/code-polish/SKILL.md"
},
{
"id": "code-refactoring-context-restore",
"name": "code-refactoring-context-restore",
@@ -46336,6 +46361,34 @@
],
"path": "skills/workflow-patterns/SKILL.md"
},
{
"id": "workorai",
"name": "workorai",
"description": "WorkorAI talent-marketplace skill: candidates search jobs and manage applications; employers run the job lifecycle and get ranked candidate matches with white-box fit explanations.",
"category": "general",
"tags": [
"job-search",
"hiring",
"recruiting",
"talent-marketplace",
"mcp"
],
"triggers": [
"job-search",
"hiring",
"recruiting",
"talent-marketplace",
"mcp",
"workorai",
"talent",
"marketplace",
"skill",
"candidates",
"search",
"jobs"
],
"path": "skills/workorai/SKILL.md"
},
{
"id": "wrike-automation",
"name": "wrike-automation",
@@ -8173,6 +8173,25 @@
},
"runtime_files": []
},
{
"id": "code-polish",
"path": "skills/code-polish",
"targets": {
"codex": "supported",
"claude": "supported"
},
"setup": {
"type": "none",
"summary": "",
"docs": null
},
"reasons": [],
"blocked_reasons": {
"codex": [],
"claude": []
},
"runtime_files": []
},
{
"id": "code-refactoring-context-restore",
"path": "skills/code-refactoring-context-restore",
@@ -35776,6 +35795,25 @@
},
"runtime_files": []
},
{
"id": "workorai",
"path": "skills/workorai",
"targets": {
"codex": "supported",
"claude": "supported"
},
"setup": {
"type": "none",
"summary": "",
"docs": null
},
"reasons": [],
"blocked_reasons": {
"codex": [],
"claude": []
},
"runtime_files": []
},
{
"id": "wrike-automation",
"path": "skills/wrike-automation",
@@ -36373,10 +36411,10 @@
}
],
"summary": {
"total_skills": 1894,
"total_skills": 1896,
"supported": {
"codex": 1827,
"claude": 1846
"codex": 1829,
"claude": 1848
},
"blocked": {
"codex": 67,
@@ -9612,6 +9612,28 @@
"reasons": []
}
},
{
"id": "code-polish",
"path": "skills/code-polish",
"category": "development",
"name": "code-polish",
"description": "Rewrites unprofessional code comments into clear ones and performs non-semantic cleanup. Use to professionalize code without altering logic or behavior.",
"risk": "critical",
"source": "community",
"date_added": "2026-07-02",
"plugin": {
"targets": {
"codex": "supported",
"claude": "supported"
},
"setup": {
"type": "none",
"summary": "",
"docs": null
},
"reasons": []
}
},
{
"id": "code-refactoring-context-restore",
"path": "skills/code-refactoring-context-restore",
@@ -41079,6 +41101,28 @@
"reasons": []
}
},
{
"id": "workorai",
"path": "skills/workorai",
"category": "productivity",
"name": "workorai",
"description": "WorkorAI talent-marketplace skill: candidates search jobs and manage applications; employers run the job lifecycle and get ranked candidate matches with white-box fit explanations.",
"risk": "critical",
"source": "community",
"date_added": "2026-07-03",
"plugin": {
"targets": {
"codex": "supported",
"claude": "supported"
},
"setup": {
"type": "none",
"summary": "",
"docs": null
},
"reasons": []
}
},
{
"id": "wrike-automation",
"path": "skills/wrike-automation",
@@ -1,9 +1,9 @@
---
title: Jetski/Cortex + Gemini Integration Guide
description: "Use antigravity-awesome-skills with Jetski/Cortex without hitting context-window overflow with 1,894+ skills."
description: "Use antigravity-awesome-skills with Jetski/Cortex without hitting context-window overflow with 1,896+ skills."
---
# Jetski/Cortex + Gemini: safe integration with 1,894+ skills
# Jetski/Cortex + Gemini: safe integration with 1,896+ skills
This guide shows how to integrate the `antigravity-awesome-skills` repository with an agent based on **Jetski/Cortex + Gemini** (or similar frameworks) **without exceeding the model context window**.
@@ -23,7 +23,7 @@ Never do:
- concatenate all `SKILL.md` content into a single system prompt;
- re-inject the entire library for **every** request.
With 1,894+ skills, this approach fills the context window before user messages are even added, causing truncation.
With 1,896+ skills, this approach fills the context window before user messages are even added, causing truncation.
---
@@ -21,7 +21,7 @@ This example shows one way to integrate **antigravity-awesome-skills** with a Je
- How to enforce a **maximum number of skills per turn** via `maxSkillsPerTurn`.
- How to choose whether to **truncate or error** when too many skills are requested via `overflowBehavior`.
This pattern avoids context overflow when you have 1,894+ skills installed.
This pattern avoids context overflow when you have 1,896+ skills installed.
Manifest contract references:
@@ -6,7 +6,7 @@ This document keeps the repository's GitHub-facing discovery copy aligned with t
Preferred positioning:
> Installable GitHub library of 1,894+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.
> Installable GitHub library of 1,896+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.
Key framing:
@@ -20,7 +20,7 @@ Key framing:
Preferred description:
> Installable GitHub library of 1,894+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.
> Installable GitHub library of 1,896+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.
Preferred homepage:
@@ -28,7 +28,7 @@ Preferred homepage:
Preferred social preview:
- use a clean preview image that says `1,894+ Agentic Skills`;
- use a clean preview image that says `1,896+ Agentic Skills`;
- mention Claude Code, Cursor, Codex CLI, and Gemini CLI;
- avoid dense text and tiny logos that disappear in social cards.
@@ -72,7 +72,7 @@ The update process refreshes:
- Canonical skills index (`skills_index.json`)
- Compatibility mirror (`data/skills_index.json`)
- Web app skills data (`apps\web-app\public\skills.json`)
- All 1,894+ skills from the skills directory
- All 1,896+ skills from the skills directory
## When to Update
@@ -1061,4 +1061,4 @@ Found a skill that should be in a bundle? Or want to create a new bundle? [Open
---
_Last updated: June 2026 | Total Skills: 1,894+ | Total Bundles: 59_
_Last updated: June 2026 | Total Skills: 1,896+ | Total Bundles: 59_
@@ -12,7 +12,7 @@ Install the library into Claude Code, then invoke focused skills directly in the
## Why use this repo for Claude Code
- It includes 1,894+ skills instead of a narrow single-domain starter pack.
- It includes 1,896+ skills instead of a narrow single-domain starter pack.
- It supports the standard `.claude/skills/` path and the Claude Code plugin marketplace flow.
- It also ships generated bundle plugins so teams can install focused packs like `Essentials` or `Security Developer` from the marketplace metadata.
- It includes onboarding docs, bundles, and workflows so new users do not need to guess where to begin.
@@ -12,7 +12,7 @@ Install into the Gemini skills path, then ask Gemini to apply one skill at a tim
- It installs directly into the expected Gemini skills path.
- It includes both core software engineering skills and deeper agent/LLM-oriented skills.
- It helps new users get started with bundles and workflows rather than forcing a cold start from 1,894+ files.
- It helps new users get started with bundles and workflows rather than forcing a cold start from 1,896+ files.
- It is useful whether you want a broad internal skill library or a single repo to test many workflows quickly.
## Install Gemini CLI Skills
@@ -1,4 +1,4 @@
# Getting Started with Antigravity Awesome Skills (V13.7.0)
# Getting Started with Antigravity Awesome Skills (V13.9.0)
**New here? This guide will help you supercharge your AI Agent in 5 minutes.**
@@ -18,7 +18,7 @@ Kiro is AWS's agentic AI IDE that combines:
Kiro's agentic capabilities are enhanced by skills that provide:
- **Domain expertise** across 1,894+ specialized areas
- **Domain expertise** across 1,896+ specialized areas
- **Best practices** from Anthropic, OpenAI, Google, Microsoft, and AWS
- **Workflow automation** for common development tasks
- **AWS-specific patterns** for serverless, infrastructure, and cloud architecture
@@ -14,7 +14,7 @@ If you came in through a **Claude Code** or **Codex** plugin instead of a full l
When you ran `npx antigravity-awesome-skills` or cloned the repository, you:
**Downloaded 1,894+ skill files** to your computer (default: `~/.agents/skills/`; or a custom path like `~/.agent/skills/` if you used `--path`)
**Downloaded 1,896+ skill files** to your computer (default: `~/.agents/skills/`; or a custom path like `~/.agent/skills/` if you used `--path`)
**Made them available** to your AI assistant
**Did NOT enable them all automatically** (they're just sitting there, waiting)
@@ -34,7 +34,7 @@ Bundles are **curated groups** of skills organized by role. They help you decide
**Analogy:**
- You installed a toolbox with 1,894+ tools (✅ done)
- You installed a toolbox with 1,896+ tools (✅ done)
- Bundles are like **labeled organizer trays** saying: "If you're a carpenter, start with these 10 tools"
- You can either **pick skills from the tray** or install that tray as a focused marketplace bundle plugin
@@ -212,7 +212,7 @@ Let's actually use a skill right now. Follow these steps:
## Step 5: Picking Your First Skills (Practical Advice)
Don't try to use all 1,894+ skills at once. Here's a sensible approach:
Don't try to use all 1,896+ skills at once. Here's a sensible approach:
If you want a tool-specific starting point before choosing skills, use:
@@ -343,7 +343,7 @@ Usually no, but if your AI doesn't recognize a skill:
### "Can I load all skills into the model at once?"
No. Even though you have 1,894+ skills installed locally, you should **not** concatenate every `SKILL.md` into a single system prompt or context block.
No. Even though you have 1,896+ skills installed locally, you should **not** concatenate every `SKILL.md` into a single system prompt or context block.
The intended pattern is:
@@ -34,7 +34,7 @@ antigravity-awesome-skills/
├── 📄 CONTRIBUTING.md ← Contributor workflow
├── 📄 CATALOG.md ← Full generated catalog
├── 📁 skills/ ← 1,894+ skills live here
├── 📁 skills/ ← 1,896+ skills live here
│ │
│ ├── 📁 brainstorming/
│ │ └── 📄 SKILL.md ← Skill definition
@@ -47,7 +47,7 @@ antigravity-awesome-skills/
│ │ └── 📁 2d-games/
│ │ └── 📄 SKILL.md ← Nested skills also supported
│ │
│ └── ... (1,894+ total)
│ └── ... (1,896+ total)
├── 📁 apps/
│ └── 📁 web-app/ ← Interactive browser
@@ -100,7 +100,7 @@ antigravity-awesome-skills/
```
┌─────────────────────────┐
│ 1,894+ SKILLS │
│ 1,896+ SKILLS │
└────────────┬────────────┘
┌────────────────────────┼────────────────────────┐
@@ -201,7 +201,7 @@ If you want a workspace-style manual install instead, cloning into `.agent/skill
│ ├── 📁 brainstorming/ │
│ ├── 📁 stripe-integration/ │
│ ├── 📁 react-best-practices/ │
│ └── ... (1,894+ total) │
│ └── ... (1,896+ total) │
└─────────────────────────────────────────┘
```
+27 -2
View File
@@ -1,20 +1,45 @@
{
"name": "antigravity-awesome-skills",
"version": "13.7.0",
"version": "13.9.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "antigravity-awesome-skills",
"version": "13.7.0",
"version": "13.9.0",
"license": "MIT",
"dependencies": {
"sanitize-filename": "^1.6.4",
"yaml": "^2.9.0"
},
"bin": {
"antigravity-awesome-skills": "tools/bin/install.js"
}
},
"node_modules/sanitize-filename": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz",
"integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==",
"license": "WTFPL OR ISC",
"dependencies": {
"truncate-utf8-bytes": "^1.0.0"
}
},
"node_modules/truncate-utf8-bytes": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
"integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==",
"license": "WTFPL",
"dependencies": {
"utf8-byte-length": "^1.0.1"
}
},
"node_modules/utf8-byte-length": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
"integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==",
"license": "(WTFPL OR MIT)"
},
"node_modules/yaml": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
+3 -2
View File
@@ -1,7 +1,7 @@
{
"name": "antigravity-awesome-skills",
"version": "13.7.0",
"description": "1,894+ agentic skills for Claude Code, Gemini CLI, Cursor, Antigravity & more. Installer CLI.",
"version": "13.9.0",
"description": "1,896+ agentic skills for Claude Code, Gemini CLI, Cursor, Antigravity & more. Installer CLI.",
"license": "MIT",
"scripts": {
"validate": "node tools/scripts/run-python.js tools/scripts/validate_skills.py",
@@ -68,6 +68,7 @@
"app:preview": "cd apps/web-app && npm run preview"
},
"dependencies": {
"sanitize-filename": "^1.6.4",
"yaml": "^2.9.0"
},
"repository": {
@@ -1,7 +1,7 @@
{
"name": "antigravity-awesome-skills",
"version": "13.7.0",
"description": "Plugin-safe Claude Code distribution of Antigravity Awesome Skills with 1,846 supported skills.",
"version": "13.9.0",
"description": "Plugin-safe Claude Code distribution of Antigravity Awesome Skills with 1,848 supported skills.",
"author": {
"name": "sickn33 and contributors",
"url": "https://github.com/sickn33/antigravity-awesome-skills"
@@ -27,6 +27,19 @@ import time
from datetime import datetime, timezone
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
# ---------------------------------------------------------------------------
# Imports from the 007 config hub (same directory)
# ---------------------------------------------------------------------------
@@ -397,12 +410,11 @@ def _phase1_surface_mapping(target: Path, verbose: bool = False) -> dict:
_config_extensions = {".json", ".yaml", ".yml", ".toml", ".ini", ".cfg", ".conf", ".env"}
for root, dirs, filenames in os.walk(target):
dirs[:] = [d for d in dirs if d not in SKIP_DIRECTORIES]
for fname in filenames:
for fpath in safe_user_path(target).rglob("*"):
if not fpath.is_file() or any(part in SKIP_DIRECTORIES for part in fpath.parts):
continue
fname = fpath.name
total_files += 1
fpath = Path(root) / fname
suffix = fpath.suffix.lower()
# Categorize by extension
@@ -1053,7 +1065,7 @@ def run_audit(
ensure_directories()
target = Path(target_path).resolve()
target = safe_user_path(target_path).resolve()
if not target.exists():
logger.error("Target path does not exist: %s", target)
sys.exit(1)
@@ -17,6 +17,19 @@ import sys
import time
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
# ---------------------------------------------------------------------------
# Imports from the 007 config hub (same directory)
# ---------------------------------------------------------------------------
@@ -134,18 +147,14 @@ def collect_files(target: Path, logger) -> list[Path]:
files: list[Path] = []
max_files = LIMITS["max_files_per_scan"]
for root, dirs, filenames in os.walk(target):
# Prune skipped directories in-place so os.walk does not descend
dirs[:] = [d for d in dirs if not _should_skip_dir(d)]
for fname in filenames:
for fpath in safe_user_path(target).rglob("*"):
if not fpath.is_file() or any(_should_skip_dir(part) for part in fpath.parts):
continue
if len(files) >= max_files:
logger.warning(
"Reached max_files_per_scan limit (%d). Stopping collection.", max_files
)
return files
fpath = Path(root) / fname
if _is_scannable(fpath):
files.append(fpath)
@@ -368,7 +377,7 @@ def run_scan(target_path: str, output_format: str = "text", verbose: bool = Fals
logger = setup_logging("007-quick-scan")
ensure_directories()
target = Path(target_path).resolve()
target = safe_user_path(target_path).resolve()
if not target.exists():
logger.error("Target path does not exist: %s", target)
sys.exit(1)
@@ -17,6 +17,19 @@ import sys
import time
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
# ---------------------------------------------------------------------------
# Import from the 007 config hub (parent directory)
# ---------------------------------------------------------------------------
@@ -850,11 +863,10 @@ def discover_dependency_files(target: Path) -> list[Path]:
"""
found: list[Path] = []
for root, dirs, filenames in os.walk(target):
dirs[:] = [d for d in dirs if d not in config.SKIP_DIRECTORIES]
for fname in filenames:
fpath = Path(root) / fname
for fpath in safe_user_path(target).rglob("*"):
if not fpath.is_file() or any(part in config.SKIP_DIRECTORIES for part in fpath.parts):
continue
fname = fpath.name
fname_lower = fname.lower()
# Exact name matches
@@ -1158,7 +1170,7 @@ def run_scan(
config.ensure_directories()
target = Path(target_path).resolve()
target = safe_user_path(target_path).resolve()
if not target.exists():
logger.error("Target path does not exist: %s", target)
sys.exit(1)
@@ -20,6 +20,19 @@ import sys
import time
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
# ---------------------------------------------------------------------------
# Import from the 007 config hub (parent directory)
# ---------------------------------------------------------------------------
@@ -546,17 +559,14 @@ def collect_files(target: Path) -> list[Path]:
files: list[Path] = []
max_files = config.LIMITS["max_files_per_scan"]
for root, dirs, filenames in os.walk(target):
dirs[:] = [d for d in dirs if d not in config.SKIP_DIRECTORIES]
for fname in filenames:
for fpath in safe_user_path(target).rglob("*"):
if not fpath.is_file() or any(part in config.SKIP_DIRECTORIES for part in fpath.parts):
continue
if len(files) >= max_files:
logger.warning(
"Reached max_files_per_scan limit (%d). Stopping.", max_files
)
return files
fpath = Path(root) / fname
if _should_scan_file(fpath):
files.append(fpath)
@@ -961,7 +971,7 @@ def run_scan(
config.ensure_directories()
target = Path(target_path).resolve()
target = safe_user_path(target_path).resolve()
if not target.exists():
logger.error("Target path does not exist: %s", target)
sys.exit(1)
@@ -20,6 +20,19 @@ import sys
import time
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
# ---------------------------------------------------------------------------
# Import from the 007 config hub (parent directory)
# ---------------------------------------------------------------------------
@@ -375,17 +388,14 @@ def collect_files(target: Path) -> list[Path]:
files: list[Path] = []
max_files = config.LIMITS["max_files_per_scan"]
for root, dirs, filenames in os.walk(target):
dirs[:] = [d for d in dirs if d not in config.SKIP_DIRECTORIES]
for fname in filenames:
for fpath in safe_user_path(target).rglob("*"):
if not fpath.is_file() or any(part in config.SKIP_DIRECTORIES for part in fpath.parts):
continue
if len(files) >= max_files:
logger.warning(
"Reached max_files_per_scan limit (%d). Stopping.", max_files
)
return files
fpath = Path(root) / fname
if _should_scan_file(fpath):
files.append(fpath)
@@ -869,7 +879,7 @@ def run_scan(
config.ensure_directories()
target = Path(target_path).resolve()
target = safe_user_path(target_path).resolve()
if not target.exists():
logger.error("Target path does not exist: %s", target)
sys.exit(1)
@@ -24,6 +24,19 @@ import sys
import time
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
# ---------------------------------------------------------------------------
# Imports from the 007 config hub (same directory)
# ---------------------------------------------------------------------------
@@ -141,12 +154,11 @@ def _collect_source_files(target: Path) -> list[Path]:
files: list[Path] = []
max_files = LIMITS["max_files_per_scan"]
for root, dirs, filenames in os.walk(target):
dirs[:] = [d for d in dirs if d not in SKIP_DIRECTORIES]
for fname in filenames:
for fpath in safe_user_path(target).rglob("*"):
if not fpath.is_file() or any(part in SKIP_DIRECTORIES for part in fpath.parts):
continue
if len(files) >= max_files:
return files
fpath = Path(root) / fname
suffix = fpath.suffix.lower()
name = fpath.name.lower()
for ext in SCANNABLE_EXTENSIONS:
@@ -529,7 +541,7 @@ def run_score(
ensure_directories()
target = Path(target_path).resolve()
target = safe_user_path(target_path).resolve()
if not target.exists():
logger.error("Target path does not exist: %s", target)
sys.exit(1)
@@ -14,6 +14,20 @@ import socket
import requests
from urllib.parse import urlparse
from typing import Optional, Dict, Any
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
API_BASE_URL = "https://2slides.com/api/v1"
@@ -124,7 +138,7 @@ def download_slides_pages_voices(
zip_response.raise_for_status()
# Save to file
with open(output_path, 'wb') as f:
with safe_user_path(output_path).open('wb') as f:
for chunk in zip_response.iter_content(chunk_size=8192):
f.write(chunk)
@@ -12,6 +12,19 @@ import shutil
from datetime import datetime
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
# Rich for beautiful terminal output
try:
from rich.console import Console
@@ -386,9 +399,9 @@ def save_outputs(transcript_text, ata_text, audio_file, output_dir="."):
# Sempre salva transcript
transcript_filename = f"transcript-{timestamp}.md"
transcript_path = Path(output_dir) / transcript_filename
transcript_path = safe_user_path(output_dir) / transcript_filename
with open(transcript_path, 'w', encoding='utf-8') as f:
with transcript_path.open('w', encoding='utf-8') as f:
f.write(transcript_text)
console.print(f"[green]✅ Transcript salvo:[/green] {transcript_filename}")
@@ -397,9 +410,9 @@ def save_outputs(transcript_text, ata_text, audio_file, output_dir="."):
ata_path = None
if ata_text:
ata_filename = f"ata-{timestamp}.md"
ata_path = Path(output_dir) / ata_filename
ata_path = safe_user_path(output_dir) / ata_filename
with open(ata_path, 'w', encoding='utf-8') as f:
with ata_path.open('w', encoding='utf-8') as f:
f.write(ata_text)
console.print(f"[green]✅ Ata salva:[/green] {ata_filename}")
@@ -30,6 +30,19 @@ import sys
from collections import Counter, defaultdict
from datetime import datetime, timezone
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Any
@@ -336,11 +349,11 @@ def build_report(records: list[dict[str, Any]], source_path: Path) -> str:
def main(argv: list[str]) -> int:
if len(argv) != 3:
print(f"usage: {Path(argv[0]).name} <input.ndjson> <output.md>", file=sys.stderr)
print(f"usage: {safe_user_path(argv[0]).name} <input.ndjson> <output.md>", file=sys.stderr)
return 1
input_path = Path(argv[1])
output_path = Path(argv[2])
input_path = safe_user_path(argv[1])
output_path = safe_user_path(argv[2])
if not input_path.exists() or input_path.stat().st_size == 0:
print(f"error: {input_path} is missing or empty", file=sys.stderr)
@@ -20,6 +20,19 @@ import time
from datetime import datetime
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
try:
import psutil
except ImportError:
@@ -281,8 +294,8 @@ def main():
else:
output_path = f"monitor_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
with open(output_path, "w", encoding="utf-8") as f:
json.dump(output_data, f, indent=2, ensure_ascii=False)
with safe_user_path(output_path).open("w", encoding="utf-8") as f:
f.write(json.dumps(output_data, indent=2, ensure_ascii=False))
print(f"\nLog salvo em: {output_path}\n")
@@ -0,0 +1,192 @@
---
name: code-polish
description: Rewrites unprofessional code comments into clear ones and performs non-semantic cleanup. Use to professionalize code without altering logic or behavior.
risk: critical
source: community
date_added: "2026-07-02"
---
# Code Polish
A constraint-based protocol for normalizing code comments and performing safe, non-semantic cleanup. This skill exists because human-written code tends to carry casual, outdated, or missing comments, while the goal is professional-grade documentation without touching behavior.
This file is self-contained. Do not require any other skill file to execute this protocol.
## Prime Directive
Comments and non-semantic cleanup are the job. Logic is never the job. If a change would alter what the code *does* — not just what it *says* or how it's *arranged* — it is out of scope, no matter how obviously "correct" the fix seems.
---
## When to Use
Apply this skill when:
- The user asks to "clean up," "professionalize," or "polish" existing code
- Code is being prepped for code review, handoff, open-sourcing, or documentation
- A file has a mix of human and AI-written comments and needs one consistent, professional voice
- Comments are outdated, missing, redundant, or written casually (venting, placeholders, inside jokes)
- The user wants comments improved but explicitly does **not** want logic touched
Do not apply this skill when:
- The user wants a bug fixed or behavior changed (that's a different job — logic edits are out of scope here)
- The user wants a full rewrite or architectural restructuring
- The only ask is adding new features or functionality
---
## Phase 0 — Full Read
Before editing anything, read the entire file (or the entire relevant module if the codebase is large — not just the function in question). Do not comment or clean incrementally while still reading. A comment written without full context is a guess, and guesses are how "professional" comments end up wrong.
Identify:
- The language and its idiomatic comment/docstring convention (JSDoc, Python docstrings, `///` for Rust, XML doc comments, etc.)
- Any existing project comment style already in use elsewhere in the file — match it rather than importing a foreign convention
- Any comment that encodes real, non-obvious information (race conditions, workarounds for external bugs, "don't reorder this" warnings, business-rule justifications)
---
## Phase 1 — Comment Audit
Classify every existing comment into one of these categories before touching it:
| Category | Example | Action |
|---|---|---|
| **Junk / venting** | `// wtf is this`, `// idk why but it works` | Remove tone, extract any real information underneath, rewrite professionally — or delete if it truly holds zero information |
| **Placeholder** | `// fix later`, `// TODO hack` | Convert to a proper `TODO:` note with the actual concern stated plainly, or remove if stale/resolved |
| **Dead code comments** | Blocks of commented-out code | Remove, unless the surrounding context makes clear it's intentionally preserved (e.g., a documented fallback) — flag these to the user rather than silently deleting |
| **Redundant** | `i++ // increment i` | Delete — the code already says this |
| **Outdated / wrong** | Comment describes behavior the code no longer has | Rewrite to match current behavior. Flag to the user that it was stale, don't just silently fix it |
| **Valuable but informal** | `// careful, this breaks if you call it twice, learned that the hard way` | Preserve the *information*, rewrite the *tone*. Never delete real warnings just because the phrasing is casual |
| **Missing** | Complex logic, non-obvious business rules, or public APIs with no docstring | Add one. Don't over-comment simple, self-explanatory lines |
---
## Phase 2 — Non-Semantic Cleanup
Scope is strictly limited to changes that cannot alter behavior:
- Consistent indentation and whitespace
- Consistent brace/bracket style matching the surrounding file
- Removing truly dead code (unreachable blocks) — only when unambiguous, and flagged in the summary
- Splitting overly long lines for readability
- Local variable renaming for clarity is allowed **only** for private/local-scope names, and only when the improvement is unambiguous — never rename anything exported, public, or referenced across files without calling it out explicitly first
Anything beyond this — reordering logic, extracting functions, changing control flow, altering algorithms — is out of scope for this skill.
---
## Phase 3 — Comment Rewrite / Addition
Apply these standards to every comment touched or added:
- **Explain why, not what.** The code already shows *what* it does; a comment earns its place by explaining intent, tradeoffs, or non-obvious constraints.
- **Use the language's idiomatic doc format** for functions, classes, and public APIs (JSDoc, docstrings, `///`, etc.) — match the convention already used elsewhere in the file if one exists.
- **Be concise.** No padding, no restating the obvious, no filler sentences.
- **No informal register.** No jokes, no venting, no first-person asides ("I think this works because...").
- **No AI-tell phrasing.** Avoid generic filler like "This function is responsible for..." or "Note that..." padding, and avoid em-dashes. Write plainly and directly, the way a careful senior engineer would.
- **Don't invent behavior.** If you're not certain why something is done a certain way, say what the code does, not a fabricated justification for why.
---
## Phase 4 — Verification
Before presenting the result:
- Confirm the edited file's logic is behaviorally identical to the original — comments and whitespace are the only permitted diffs, plus whatever narrow Phase 2 cleanup was done.
- Re-read the diff end to end, not just the changed lines in isolation, to catch anything that accidentally shifted meaning.
- If a rewritten comment removes information that was present in the original (even informally stated), that's a failure — go back and preserve it.
---
## Phase 5 — Report Back
Summarize for the user, don't just hand back a silent diff:
- How many comments were rewritten, added, or removed, and why
- Any comments flagged as "informal but contained a real warning" — confirm the information was preserved
- Any dead code or stale comments removed, listed explicitly
- Anything you were unsure about and left alone rather than guessing
---
## Examples
**Junk / venting → professional**
```js
// before
// ugh this took forever to figure out. api rate limits us super hard in prod so we have to do exponential backoff here. just leave it alone
function retryFetch(url, attempts) { ... }
// after
// Uses exponential backoff to handle aggressive API rate-limiting in production.
function retryFetch(url, attempts) { ... }
```
**Redundant → removed**
```python
# before
count += 1 # increment count by 1
# after
count += 1
```
**Valuable but informal → tone rewritten, information preserved**
```python
# before
# careful, this breaks if you call it twice, learned that the hard way
# after
# Not idempotent: calling this more than once per session corrupts the
# cache index. Callers must guard against duplicate invocation.
```
**Missing → added**
```java
// before
public double calculate(double base, int tier) {
return base * (tier > 2 ? 0.85 : 1.0);
}
// after
/**
* Applies the loyalty discount. Tiers above 2 qualify for a 15% discount;
* this threshold matches the current pricing policy, not a technical limit.
*/
public double calculate(double base, int tier) {
return base * (tier > 2 ? 0.85 : 1.0);
}
```
**Outdated / wrong → corrected and flagged**
```go
// before
// returns nil if user not found
func GetUser(id string) (*User, error) { ... } // now returns ErrNotFound instead
// after
// Returns ErrNotFound if the user does not exist.
func GetUser(id string) (*User, error) { ... }
// (flagged to user: original comment was stale — function used to return nil,
// now returns a named error)
```
---
## Security & Safety Notes
This skill never:
- Changes program logic, control flow, or algorithmic behavior
- Restructures code (extracting/inlining functions, reordering execution, changing architecture)
- Renames anything public, exported, or cross-referenced without explicit confirmation
- Deletes a comment solely because its tone is casual, without checking whether it carries real information first
- Fabricates a rationale for a comment when the actual reason isn't knowable from context — state what's certain only
---
## Limitations
- Cannot verify runtime behavior — Phase 4 is a read-through diff check, not a test run. For anything beyond trivial files, the user should still run the actual test suite after applying this skill.
- Judgment calls on ambiguous cases (e.g., "is this dead code intentional or forgotten?") default to flagging rather than guessing — this means some cleanup will need a quick human yes/no rather than happening silently.
- Not a substitute for a linter or formatter — Phase 2 cleanup is deliberately conservative and won't enforce a full style guide (e.g., max line length rules, import ordering) unless that's trivially inferable from the surrounding file.
- Comment quality is bounded by how well the code's actual intent can be inferred from context. If the "why" genuinely isn't recoverable from the file (no domain knowledge, no commit history, no ticket references available), the honest output is a comment describing *what*, not a confident but invented *why*.
- Large files or unfamiliar codebases increase the risk of Phase 0 missing context that would have changed a comment's wording — flag uncertainty in the Phase 5 report rather than presenting low-confidence rewrites as settled.
@@ -12,12 +12,33 @@
//
// Usage: node capture_screenshots.mjs <research-dir> [--mode remote|local] [--concurrency 2]
import sanitizeFilename from 'sanitize-filename';
import { readdirSync, readFileSync, mkdirSync, existsSync } from 'fs';
import { join } from 'path';
import { isAbsolute, join, relative, resolve } from 'path';
import { spawnSync } from 'child_process';
import { parseFrontmatter } from './md_utils.mjs';
const args = process.argv.slice(2);
function sanitizePathSegments(pathValue) {
return String(pathValue ?? '').split(/[\\/]+/).filter(Boolean).map((segment) => {
const sanitized = sanitizeFilename(segment);
if (sanitized !== segment || !sanitized) {
throw new Error(`Unsafe path segment: ${segment}`);
}
return sanitized;
});
}
function safeCliPath(pathValue, baseDir = process.cwd()) {
const root = resolve(baseDir);
const target = resolve(root, ...sanitizePathSegments(pathValue));
const rel = relative(root, target);
if (rel.startsWith('..') || isAbsolute(rel)) {
throw new Error(`Path escapes allowed directory: ${pathValue}`);
}
return target;
}
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
console.error(`Usage: node capture_screenshots.mjs <research-dir> [options]
@@ -38,7 +59,7 @@ Options:
process.exit(args.includes('--help') || args.includes('-h') ? 0 : 1);
}
const dir = args[0];
const dir = safeCliPath(args[0]);
const modeIdx = args.indexOf('--mode');
const browseMode = modeIdx !== -1 ? args[modeIdx + 1] : 'remote';
const modeFlag = browseMode === 'local' ? '--local' : '--remote';
@@ -63,7 +84,7 @@ if (concurrency > 1) {
concurrency = 1;
}
const shotsDir = join(dir, 'screenshots');
const shotsDir = safeCliPath('screenshots', dir);
mkdirSync(shotsDir, { recursive: true });
function run(cmd, args, { timeout = 30000 } = {}) {
@@ -9,6 +9,7 @@
import { readdirSync, readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
import { basename, dirname, join, relative, resolve } from 'path';
import { fileURLToPath } from 'url';
import sanitizeFilename from 'sanitize-filename';
import { parseFrontmatter, parseBody, parseSections } from './md_utils.mjs';
const __filename = fileURLToPath(import.meta.url);
@@ -17,9 +18,19 @@ const __dirname = dirname(__filename);
const args = process.argv.slice(2);
const SAFE_SLUG_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
function sanitizePathSegments(pathValue) {
return String(pathValue ?? '').split(/[\\/]+/).filter(Boolean).map((segment) => {
const sanitized = sanitizeFilename(segment);
if (sanitized !== segment || !sanitized) {
throw new Error(`Unsafe path segment: ${segment}`);
}
return sanitized;
});
}
function safeJoin(base, ...parts) {
const root = resolve(base);
const target = resolve(root, ...parts);
const target = resolve(root, ...parts.flatMap(sanitizePathSegments));
const rel = relative(root, target);
if (rel.startsWith('..') || rel.startsWith('/')) {
throw new Error(`Path escapes research directory: ${parts.join('/')}`);
@@ -32,7 +43,7 @@ function safeResearchDir(rawDir) {
throw new Error('Research directory is required');
}
const root = resolve(process.cwd());
const target = resolve(root, rawDir);
const target = safeJoin(root, rawDir);
const rel = relative(root, target);
if ((rel.startsWith('..') || rel.startsWith('/')) && process.env.COMPETITOR_ANALYSIS_ALLOW_EXTERNAL_DIR !== '1') {
throw new Error('Research directory must stay under the current working directory');
@@ -9,10 +9,31 @@
// Output: newline-delimited JSON to stdout, one object per candidate:
// { "name": "serper", "hits": 3, "domain": "serper.dev", "example": "Tavily vs Serper..." }
import sanitizeFilename from 'sanitize-filename';
import { readdirSync, readFileSync } from 'fs';
import { join } from 'path';
import { isAbsolute, join, relative, resolve } from 'path';
const args = process.argv.slice(2);
function sanitizePathSegments(pathValue) {
return String(pathValue ?? '').split(/[\\/]+/).filter(Boolean).map((segment) => {
const sanitized = sanitizeFilename(segment);
if (sanitized !== segment || !sanitized) {
throw new Error(`Unsafe path segment: ${segment}`);
}
return sanitized;
});
}
function safeCliPath(pathValue, baseDir = process.cwd()) {
const root = resolve(baseDir);
const target = resolve(root, ...sanitizePathSegments(pathValue));
const rel = relative(root, target);
if (rel.startsWith('..') || isAbsolute(rel)) {
throw new Error(`Path escapes allowed directory: ${pathValue}`);
}
return target;
}
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
console.error(`Usage: node extract_vs_names.mjs <directory> [--prefix <prefix>] [--seed "<csv>"]
@@ -28,7 +49,7 @@ Options:
process.exit(args.includes('--help') || args.includes('-h') ? 0 : 1);
}
const dir = args[0];
const dir = safeCliPath(args[0]);
const prefixIdx = args.indexOf('--prefix');
const prefix = prefixIdx !== -1 && args[prefixIdx + 1] ? args[prefixIdx + 1] : 'competitor';
const seedIdx = args.indexOf('--seed');
@@ -14,6 +14,7 @@
// { "url": "https://foo.com", "status": "PASS" | "REJECT" | "UNKNOWN",
// "matched_includes": [...], "matched_excludes": [...], "title": "...", "hero": "..." }
import sanitizeFilename from 'sanitize-filename';
import { execFile } from 'child_process';
import { promisify } from 'util';
import { readFileSync } from 'fs';
@@ -25,6 +26,26 @@ import { readFileSync } from 'fs';
const execFileAsync = promisify(execFile);
const args = process.argv.slice(2);
function sanitizePathSegments(pathValue) {
return String(pathValue ?? '').split(/[\\/]+/).filter(Boolean).map((segment) => {
const sanitized = sanitizeFilename(segment);
if (sanitized !== segment || !sanitized) {
throw new Error(`Unsafe path segment: ${segment}`);
}
return sanitized;
});
}
function safeCliPath(pathValue, baseDir = process.cwd()) {
const root = resolve(baseDir);
const target = resolve(root, ...sanitizePathSegments(pathValue));
const rel = relative(root, target);
if (rel.startsWith('..') || isAbsolute(rel)) {
throw new Error(`Path escapes allowed directory: ${pathValue}`);
}
return target;
}
if (args.includes('--help') || args.includes('-h')) {
console.error(`Usage: cat urls.txt | node gate_candidates.mjs [options]
@@ -5,10 +5,31 @@
// Reads all {prefix}_discovery_batch_*.json files, deduplicates by domain,
// outputs one URL per line to stdout, stats to stderr.
import sanitizeFilename from 'sanitize-filename';
import { readdirSync, readFileSync } from 'fs';
import { join } from 'path';
import { isAbsolute, join, relative, resolve } from 'path';
const args = process.argv.slice(2);
function sanitizePathSegments(pathValue) {
return String(pathValue ?? '').split(/[\\/]+/).filter(Boolean).map((segment) => {
const sanitized = sanitizeFilename(segment);
if (sanitized !== segment || !sanitized) {
throw new Error(`Unsafe path segment: ${segment}`);
}
return sanitized;
});
}
function safeCliPath(pathValue, baseDir = process.cwd()) {
const root = resolve(baseDir);
const target = resolve(root, ...sanitizePathSegments(pathValue));
const rel = relative(root, target);
if (rel.startsWith('..') || isAbsolute(rel)) {
throw new Error(`Path escapes allowed directory: ${pathValue}`);
}
return target;
}
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
console.error(`Usage: node list_urls.mjs <directory> [--prefix <prefix>]
@@ -26,7 +47,7 @@ Examples:
process.exit(args.includes('--help') || args.includes('-h') ? 0 : 1);
}
const dir = args[0];
const dir = safeCliPath(args[0]);
const prefixIdx = args.indexOf('--prefix');
const prefix = prefixIdx !== -1 && args[prefixIdx + 1] ? args[prefixIdx + 1] : 'competitor';
@@ -18,11 +18,32 @@
//
// Usage: node merge_partials.mjs <research-dir>
import sanitizeFilename from 'sanitize-filename';
import { readdirSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
import { join } from 'path';
import { isAbsolute, join, relative, resolve } from 'path';
import { parseFrontmatter, parseBody, parseSections } from './md_utils.mjs';
const args = process.argv.slice(2);
function sanitizePathSegments(pathValue) {
return String(pathValue ?? '').split(/[\\/]+/).filter(Boolean).map((segment) => {
const sanitized = sanitizeFilename(segment);
if (sanitized !== segment || !sanitized) {
throw new Error(`Unsafe path segment: ${segment}`);
}
return sanitized;
});
}
function safeCliPath(pathValue, baseDir = process.cwd()) {
const root = resolve(baseDir);
const target = resolve(root, ...sanitizePathSegments(pathValue));
const rel = relative(root, target);
if (rel.startsWith('..') || isAbsolute(rel)) {
throw new Error(`Path escapes allowed directory: ${pathValue}`);
}
return target;
}
if (args.includes('--help') || args.includes('-h') || args.length === 0) {
console.error(`Usage: node merge_partials.mjs <research-dir>
@@ -31,8 +52,8 @@ Reads {dir}/partials/{slug}.{lane}.md files and writes consolidated
process.exit(args.includes('--help') || args.includes('-h') ? 0 : 1);
}
const dir = args[0];
const partialsDir = join(dir, 'partials');
const dir = safeCliPath(args[0]);
const partialsDir = safeCliPath('partials', dir);
const LANES = ['marketing', 'discussion', 'social', 'news', 'technical', 'battle'];
@@ -6,6 +6,20 @@ Brand Voice Analyzer - Analyzes content to establish and maintain brand voice co
import re
from typing import Dict, List, Tuple
import json
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
class BrandVoiceAnalyzer:
def __init__(self):
@@ -176,7 +190,7 @@ if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
with open(sys.argv[1], 'r') as f:
with safe_user_path(sys.argv[1]).open('r') as f:
content = f.read()
output_format = sys.argv[2] if len(sys.argv) > 2 else 'text'
@@ -6,6 +6,20 @@ SEO Content Optimizer - Analyzes and optimizes content for SEO
import re
from typing import Dict, List, Set
import json
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
class SEOOptimizer:
def __init__(self):
@@ -408,7 +422,7 @@ if __name__ == "__main__":
import sys
if len(sys.argv) > 1:
with open(sys.argv[1], 'r') as f:
with safe_user_path(sys.argv[1]).open('r') as f:
content = f.read()
keyword = sys.argv[2] if len(sys.argv) > 2 else None
@@ -16,6 +16,19 @@ import zipfile
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
def validate_input_tree(input_dir: Path):
root = input_dir.resolve(strict=True)
for path in input_dir.rglob("*"):
@@ -27,6 +40,18 @@ def validate_input_tree(input_dir: Path):
raise ValueError(f"Refusing to pack path outside input directory: {path}") from None
def copy_tree_contents(source_dir: Path, target_dir: Path) -> None:
target_dir.mkdir(parents=True, exist_ok=True)
for source_path in source_dir.rglob("*"):
relative_path = source_path.relative_to(source_dir)
target_path = target_dir / relative_path
if source_path.is_dir():
target_path.mkdir(parents=True, exist_ok=True)
elif source_path.is_file():
target_path.parent.mkdir(parents=True, exist_ok=True)
target_path.write_bytes(source_path.read_bytes())
def main():
parser = argparse.ArgumentParser(description="Pack a directory into an Office file")
parser.add_argument("input_directory", help="Unpacked Office document directory")
@@ -65,7 +90,7 @@ def pack_document(input_dir, output_file, validate=False):
bool: True if successful, False if validation failed
"""
input_dir = Path(input_dir)
output_file = Path(output_file)
output_file = safe_user_path(output_file)
if not input_dir.is_dir():
raise ValueError(f"{input_dir} is not a directory")
@@ -76,7 +101,7 @@ def pack_document(input_dir, output_file, validate=False):
# Work in temporary directory to avoid modifying original
with tempfile.TemporaryDirectory() as temp_dir:
temp_content_dir = Path(temp_dir) / "content"
shutil.copytree(input_dir, temp_content_dir)
copy_tree_contents(input_dir, temp_content_dir)
# Process XML files to remove pretty-printing whitespace
for pattern in ["*.xml", "*.rels"]:
@@ -85,10 +110,12 @@ def pack_document(input_dir, output_file, validate=False):
# Create final Office file as zip archive
output_file.parent.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED) as zf:
temp_zip_path = Path(temp_dir) / "office.zip"
with zipfile.ZipFile(temp_zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
for f in temp_content_dir.rglob("*"):
if f.is_file():
zf.write(f, f.relative_to(temp_content_dir))
output_file.write_bytes(temp_zip_path.read_bytes())
# Validate if requested
if validate:
@@ -8,6 +8,19 @@ import sys
import zipfile
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
MAX_ARCHIVE_MEMBERS = 5000
MAX_MEMBER_SIZE = 100 * 1024 * 1024
MAX_TOTAL_UNCOMPRESSED = 512 * 1024 * 1024
@@ -30,7 +43,7 @@ def _extract_member(archive: zipfile.ZipFile, member: zipfile.ZipInfo, output_ro
return
destination.parent.mkdir(parents=True, exist_ok=True)
with archive.open(member, "r") as source, open(destination, "wb") as target:
with archive.open(member, "r") as source, safe_user_path(destination).open("wb") as target:
shutil.copyfileobj(source, target)
@@ -57,7 +70,7 @@ def _validate_archive_members(archive: zipfile.ZipFile, output_root: Path):
def extract_archive_safely(input_file: str | Path, output_dir: str | Path):
output_path = Path(output_dir)
output_path = safe_user_path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
output_root = output_path.resolve()
@@ -82,7 +95,7 @@ def main(argv: list[str] | None = None):
raise SystemExit("Usage: python unpack.py <office_file> <output_dir>")
input_file, output_dir = argv
output_path = Path(output_dir)
output_path = safe_user_path(output_dir)
extract_archive_safely(input_file, output_path)
pretty_print_xml(output_path)
@@ -20,6 +20,19 @@ import re
import sys
from dataclasses import asdict, dataclass
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Any, Iterable
CONFIG_NAME_PATTERN = re.compile(r"^drizzle(?:[.-].+)?\.config\.(?:ts|js|mjs|cjs|mts|cts)$")
@@ -169,15 +182,14 @@ def iter_config_files(
if explicit_configs:
return configs, issues
for current_root, dirnames, filenames in os.walk(root):
dirnames[:] = [name for name in dirnames if name not in SKIP_DIR_NAMES]
base = Path(current_root)
for filename in filenames:
if CONFIG_NAME_PATTERN.match(filename):
path = (base / filename).resolve()
if path not in seen:
seen.add(path)
configs.append(path)
for path in safe_user_path(root).rglob("*"):
if not path.is_file() or any(part in SKIP_DIR_NAMES for part in path.parts):
continue
if CONFIG_NAME_PATTERN.match(path.name):
resolved = path.resolve()
if resolved not in seen:
seen.add(resolved)
configs.append(resolved)
return configs, issues
@@ -283,11 +295,9 @@ def discover_dirs(args: argparse.Namespace, root: Path) -> tuple[list[Path], lis
def iter_text_files(directory: Path) -> Iterable[Path]:
for current_root, dirnames, filenames in os.walk(directory):
dirnames[:] = [name for name in dirnames if name not in SKIP_DIR_NAMES]
base = Path(current_root)
for filename in filenames:
path = base / filename
for path in safe_user_path(directory).rglob("*"):
if not path.is_file() or any(part in SKIP_DIR_NAMES for part in path.parts):
continue
if path.suffix in TEXT_SUFFIXES:
yield path
@@ -696,7 +706,7 @@ def report_as_text(root: Path, reports: list[DirectoryReport]) -> str:
def main() -> int:
args = parse_args()
root = Path(args.root).resolve()
root = safe_user_path(args.root).resolve()
dirs, discovery_issues = discover_dirs(args, root)
reports: list[DirectoryReport] = []
if discovery_issues:
@@ -15,8 +15,12 @@
const fs = require('fs');
const path = require('path');
const sanitizeFilename = require('sanitize-filename');
const projectPath = process.argv[2];
const rawProjectPath = process.argv[2];
const projectPath = rawProjectPath
? path.resolve(process.cwd(), sanitizeFilename(path.basename(rawProjectPath)))
: null;
const withDocs = process.argv.includes('--docs');
if (!projectPath) {
@@ -13,6 +13,20 @@ import json
import os
import sys
from pptx import Presentation
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
def extract_pptx(file_path, output_dir="."):
@@ -54,7 +68,7 @@ def extract_pptx(file_path, output_dir="."):
image_name = f"slide{slide_num + 1}_img{len(slide_data['images']) + 1}.{image_ext}"
image_path = os.path.join(assets_dir, image_name)
with open(image_path, "wb") as f:
with safe_user_path(image_path).open("wb") as f:
f.write(image_bytes)
slide_data["images"].append(
@@ -81,14 +95,14 @@ if __name__ == "__main__":
sys.exit(1)
input_file = sys.argv[1]
output_dir = sys.argv[2] if len(sys.argv) > 2 else "."
output_dir = safe_user_path(sys.argv[2]) if len(sys.argv) > 2 else "."
slides = extract_pptx(input_file, output_dir)
# Write extracted data as JSON
output_path = os.path.join(output_dir, "extracted-slides.json")
with open(output_path, "w") as f:
json.dump(slides, f, indent=2)
with safe_user_path(output_path).open("w") as f:
f.write(json.dumps(slides, indent=2))
print(f"Extracted {len(slides)} slides to {output_path}")
for s in slides:
@@ -22,6 +22,20 @@ from google import genai
# Load local upload helper logic inline to prevent dependency issues
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from upload_file import upload_file, wait_for_active
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
def get_api_key(args):
"""Retrieves API key from command args or environment."""
@@ -58,6 +72,14 @@ def normalize_file_uri(uri):
return f"https://generativelanguage.googleapis.com/files/{file_id}"
return uri
def media_download_url(file_uri):
"""Build a media URL only for validated Gemini File API references."""
file_id = extract_file_id(file_uri)
if not file_id:
raise ValueError("Generated video URI must be a Gemini File API reference.")
return f"https://generativelanguage.googleapis.com/files/{file_id}?alt=media"
def slugify(text):
"""Converts a text prompt into a safe, descriptive filename slug."""
text = text.lower()
@@ -158,7 +180,7 @@ def resolve_or_upload_asset(asset_path, mime_type, api_key, strip_audio=False):
# Clean up temporary stripped file if we created one
if temp_stripped_path and os.path.exists(temp_stripped_path):
try:
os.remove(temp_stripped_path)
safe_user_path(temp_stripped_path).unlink()
print(f"Cleaned up temporary video file: {temp_stripped_path}")
except Exception as e:
print(f"Warning: Failed to remove temporary file {temp_stripped_path}: {e}", file=sys.stderr)
@@ -171,8 +193,7 @@ def resolve_or_upload_asset(asset_path, mime_type, api_key, strip_audio=False):
def download_video_file(file_uri, output_path, api_key):
"""Downloads generated video file from URI using alt=media standard in a memory-safe, chunked manner."""
separator = "&" if "?" in file_uri else "?"
download_url = f"{file_uri}{separator}alt=media"
download_url = media_download_url(file_uri)
print(f"Downloading video from {file_uri} to {output_path} in chunked mode...")
req = urllib.request.Request(download_url)
@@ -184,7 +205,7 @@ def download_video_file(file_uri, output_path, api_key):
if parent_dir:
os.makedirs(parent_dir, exist_ok=True)
with open(output_path, "wb") as f:
with safe_user_path(output_path).open("wb") as f:
while True:
chunk = resp.read(8192)
if not chunk:
@@ -357,7 +378,7 @@ def main():
print(f"Error: Batch JSON file '{args.batch}' not found.", file=sys.stderr)
sys.exit(1)
try:
with open(args.batch, "r", encoding="utf-8") as f:
with safe_user_path(args.batch).open("r", encoding="utf-8") as f:
jobs = json.load(f)
if not isinstance(jobs, list):
print("Error: Batch JSON file must contain a list/array of job objects.", file=sys.stderr)
@@ -375,7 +396,7 @@ def main():
sys.exit(1)
jobs = []
with open(args.prompts_file, "r", encoding="utf-8") as f:
with safe_user_path(args.prompts_file).open("r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if line and not line.startswith("#"):
@@ -42,6 +42,19 @@ import sys
import time
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
import polars as pl
from ascii_graph import Pyasciigraph
from datasets import Dataset
@@ -401,7 +414,7 @@ def main():
print("The 'text' column is never loaded, making this very fast.\n")
# Create output directory
output_dir = Path(args.output_dir)
output_dir = safe_user_path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
# Single scan: compute temporal stats
@@ -84,6 +84,7 @@ def run_command(cmd, description):
cmd,
check=True,
capture_output=True,
shell=False,
text=True
)
if result.stdout:
@@ -114,6 +115,14 @@ def require_hf_repo_id(value, name):
sys.exit(1)
def safe_filename_component(value, name):
"""Allow repo-name text only where it becomes a local filename component."""
if not re.fullmatch(r"[A-Za-z0-9._-]{1,96}", value):
print(f" Invalid {name}: {value!r}. Use letters, numbers, dots, dashes, or underscores.", file=sys.stderr)
sys.exit(1)
return value
def env_flag(name):
return os.environ.get(name, "").strip().lower() in {"1", "true", "yes", "on"}
@@ -230,7 +239,7 @@ gguf_output_dir = "/tmp/gguf_output"
os.makedirs(gguf_output_dir, exist_ok=True)
convert_script = "/tmp/llama.cpp/convert_hf_to_gguf.py"
model_name = ADAPTER_MODEL.split('/')[-1]
model_name = safe_filename_component(ADAPTER_MODEL.split('/')[-1], "ADAPTER_MODEL repo name")
gguf_file = f"{gguf_output_dir}/{model_name}-f16.gguf"
print(f" Running conversion...")
@@ -6,6 +6,19 @@ import re
from collections import Counter, defaultdict
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
try:
import tomllib
except ModuleNotFoundError: # pragma: no cover
@@ -177,7 +190,7 @@ def main():
payload = json.dumps(result, indent=2, sort_keys=True)
if args.output:
output = Path(args.output)
output = safe_user_path(args.output)
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(payload + "\n", encoding="utf-8")
else:
@@ -17,6 +17,19 @@ import sys
from datetime import datetime, timezone
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
sys.path.insert(0, str(Path(__file__).parent))
_db = None
@@ -55,11 +68,9 @@ def export_json(records: list, output_dir: Path, name: str) -> Path:
output_dir.mkdir(parents=True, exist_ok=True)
ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
path = output_dir / f"instagram_{name}_{ts}.json"
with open(path, "w", encoding="utf-8") as f:
json.dump(
{"exported_at": datetime.now(timezone.utc).isoformat(), "total": len(records), "data": records},
f, ensure_ascii=False, indent=2,
)
payload = {"exported_at": datetime.now(timezone.utc).isoformat(), "total": len(records), "data": records}
with safe_user_path(path).open("w", encoding="utf-8") as f:
f.write(json.dumps(payload, ensure_ascii=False, indent=2))
print(f"[JSON] {len(records)} registros ->{path}")
return path
@@ -68,7 +79,7 @@ def export_jsonl(records: list, output_dir: Path, name: str) -> Path:
output_dir.mkdir(parents=True, exist_ok=True)
ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
path = output_dir / f"instagram_{name}_{ts}.jsonl"
with open(path, "w", encoding="utf-8") as f:
with safe_user_path(path).open("w", encoding="utf-8") as f:
for rec in records:
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
print(f"[JSONL] {len(records)} registros ->{path}")
@@ -82,7 +93,7 @@ def export_csv_file(records: list, output_dir: Path, name: str) -> Path:
output_dir.mkdir(parents=True, exist_ok=True)
ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
path = output_dir / f"instagram_{name}_{ts}.csv"
with open(path, "w", newline="", encoding="utf-8-sig") as f:
with safe_user_path(path).open("w", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=list(records[0].keys()), extrasaction="ignore")
writer.writeheader()
writer.writerows(records)
@@ -18,6 +18,19 @@ import json
import sys
from datetime import datetime, timezone
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import List, Optional
sys.path.insert(0, str(Path(__file__).parent))
@@ -31,13 +44,9 @@ def export_json(records: list, output_dir: Path, suffix: str = "") -> Path:
output_dir.mkdir(parents=True, exist_ok=True)
ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
path = output_dir / f"leiloeiros{suffix}_{ts}.json"
with open(path, "w", encoding="utf-8") as f:
json.dump(
{"exported_at": datetime.now(timezone.utc).isoformat(), "total": len(records), "data": records},
f,
ensure_ascii=False,
indent=2,
)
payload = {"exported_at": datetime.now(timezone.utc).isoformat(), "total": len(records), "data": records}
with safe_user_path(path).open("w", encoding="utf-8") as f:
f.write(json.dumps(payload, ensure_ascii=False, indent=2))
print(f"[JSON] {len(records)} registros → {path}")
return path
@@ -46,7 +55,7 @@ def export_jsonl(records: list, output_dir: Path, suffix: str = "") -> Path:
output_dir.mkdir(parents=True, exist_ok=True)
ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
path = output_dir / f"leiloeiros{suffix}_{ts}.jsonl"
with open(path, "w", encoding="utf-8") as f:
with safe_user_path(path).open("w", encoding="utf-8") as f:
for rec in records:
f.write(json.dumps(rec, ensure_ascii=False) + "\n")
print(f"[JSONL] {len(records)} registros → {path}")
@@ -62,7 +71,7 @@ def export_csv(records: list, output_dir: Path, suffix: str = "") -> Path:
ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
path = output_dir / f"leiloeiros{suffix}_{ts}.csv"
with open(path, "w", newline="", encoding="utf-8-sig") as f:
with safe_user_path(path).open("w", newline="", encoding="utf-8-sig") as f:
writer = csv.DictWriter(f, fieldnames=list(records[0].keys()), extrasaction="ignore")
writer.writeheader()
writer.writerows(records)
@@ -106,7 +115,7 @@ def main():
db = Database()
db.init()
output_dir = Path(args.output)
output_dir = safe_user_path(args.output)
estados = [e.upper() for e in args.estado] if args.estado else None
if estados:
@@ -16,6 +16,20 @@ import sys
from typing import Dict, List, Any, Optional
from datetime import datetime
import html as html_module
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
def escape(text: str) -> str:
@@ -557,7 +571,7 @@ def main():
output = generate_html(config)
if args.output:
with open(args.output, "w") as f:
with safe_user_path(args.output).open("w") as f:
f.write(output)
print(f"Landing page written to {args.output}")
else:
@@ -18,6 +18,19 @@ import json
import argparse
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
# Fix Unicode output on Windows (cp1252 terminal)
if sys.platform == 'win32':
try:
@@ -498,7 +511,7 @@ def main():
analyzer.print_report(report)
if args.output:
output_path = Path(args.output)
output_path = safe_user_path(args.output)
if args.json:
output_path.write_text(json.dumps(report, indent=2, ensure_ascii=False))
else:
@@ -16,6 +16,19 @@ import sys
import json
import argparse
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from dataclasses import dataclass, field
from typing import Dict, List, Set, Optional, Tuple
@@ -518,14 +531,14 @@ def main():
output = analyzer.to_dot()
print(output)
if args.output:
Path(args.output).write_text(output)
safe_user_path(args.output).write_text(output)
print(f"\n✅ Arquivo DOT salvo: {args.output}")
print(" Para visualizar: dot -Tpng deps.dot -o deps.png")
else:
analyzer.print_report(report)
if args.output and args.format != 'dot':
Path(args.output).write_text(
safe_user_path(args.output).write_text(
json.dumps(report, indent=2, ensure_ascii=False),
encoding='utf-8'
)
@@ -71,6 +71,19 @@ import re
import json
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
class MobileAuditor:
def __init__(self):
self.issues = []
@@ -612,11 +625,12 @@ class MobileAuditor:
def audit_directory(self, directory: str) -> None:
extensions = {'.tsx', '.ts', '.jsx', '.js', '.dart'}
for root, dirs, files in os.walk(directory):
dirs[:] = [d for d in dirs if d not in {'node_modules', '.git', 'dist', 'build', '.next', 'ios', 'android', 'build', '.idea'}]
for file in files:
if Path(file).suffix in extensions:
self.audit_file(os.path.join(root, file))
skipped = {'node_modules', '.git', 'dist', 'build', '.next', 'ios', 'android', 'build', '.idea'}
for path in safe_user_path(directory).rglob("*"):
if not path.is_file() or any(part in skipped for part in path.parts):
continue
if path.suffix in extensions:
self.audit_file(str(path))
def get_report(self):
return {
@@ -633,7 +647,7 @@ def main():
print("Usage: python mobile_audit.py <directory>")
sys.exit(1)
path = sys.argv[1]
path = safe_user_path(sys.argv[1])
is_json = "--json" in sys.argv
auditor = MobileAuditor()
@@ -11,14 +11,6 @@ def _allow_external_paths() -> bool:
return os.getenv("MCD_ALLOW_EXTERNAL_PATHS", "").lower() in {"1", "true", "yes"}
def _is_relative_to(path: Path, root: Path) -> bool:
try:
path.relative_to(root)
return True
except ValueError:
return False
def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_parent: bool = False) -> Path:
value = str(raw_path).strip()
if not value or "\0" in value:
@@ -26,8 +18,13 @@ def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_pare
base = Path.cwd().resolve()
candidate = Path(value).expanduser()
resolved = (candidate if candidate.is_absolute() else base / candidate).resolve()
if not _allow_external_paths() and not _is_relative_to(resolved, base):
raise ValueError(f"Path must stay under the current working directory: {raw_path!r}")
if not _allow_external_paths():
try:
resolved.relative_to(base)
except ValueError as exc:
raise ValueError(
f"Path must stay under the current working directory: {raw_path!r}"
) from exc
if expect_file and not resolved.is_file():
raise FileNotFoundError(f"Input file not found: {resolved}")
if create_parent:
@@ -62,5 +59,5 @@ def read_json_file(raw_path: str):
def write_json_file(raw_path: str, payload, *, indent: int = 2, default=None) -> None:
with safe_output_json_path(raw_path).open("w") as fh:
json.dump(payload, fh, indent=indent, default=default)
output_path = safe_output_json_path(raw_path)
output_path.write_text(json.dumps(payload, indent=indent, default=default), encoding="utf-8")
@@ -11,14 +11,6 @@ def _allow_external_paths() -> bool:
return os.getenv("MCD_ALLOW_EXTERNAL_PATHS", "").lower() in {"1", "true", "yes"}
def _is_relative_to(path: Path, root: Path) -> bool:
try:
path.relative_to(root)
return True
except ValueError:
return False
def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_parent: bool = False) -> Path:
value = str(raw_path).strip()
if not value or "\0" in value:
@@ -26,8 +18,13 @@ def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_pare
base = Path.cwd().resolve()
candidate = Path(value).expanduser()
resolved = (candidate if candidate.is_absolute() else base / candidate).resolve()
if not _allow_external_paths() and not _is_relative_to(resolved, base):
raise ValueError(f"Path must stay under the current working directory: {raw_path!r}")
if not _allow_external_paths():
try:
resolved.relative_to(base)
except ValueError as exc:
raise ValueError(
f"Path must stay under the current working directory: {raw_path!r}"
) from exc
if expect_file and not resolved.is_file():
raise FileNotFoundError(f"Input file not found: {resolved}")
if create_parent:
@@ -62,5 +59,5 @@ def read_json_file(raw_path: str):
def write_json_file(raw_path: str, payload, *, indent: int = 2, default=None) -> None:
with safe_output_json_path(raw_path).open("w") as fh:
json.dump(payload, fh, indent=indent, default=default)
output_path = safe_output_json_path(raw_path)
output_path.write_text(json.dumps(payload, indent=indent, default=default), encoding="utf-8")
@@ -11,14 +11,6 @@ def _allow_external_paths() -> bool:
return os.getenv("MCD_ALLOW_EXTERNAL_PATHS", "").lower() in {"1", "true", "yes"}
def _is_relative_to(path: Path, root: Path) -> bool:
try:
path.relative_to(root)
return True
except ValueError:
return False
def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_parent: bool = False) -> Path:
value = str(raw_path).strip()
if not value or "\0" in value:
@@ -26,8 +18,13 @@ def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_pare
base = Path.cwd().resolve()
candidate = Path(value).expanduser()
resolved = (candidate if candidate.is_absolute() else base / candidate).resolve()
if not _allow_external_paths() and not _is_relative_to(resolved, base):
raise ValueError(f"Path must stay under the current working directory: {raw_path!r}")
if not _allow_external_paths():
try:
resolved.relative_to(base)
except ValueError as exc:
raise ValueError(
f"Path must stay under the current working directory: {raw_path!r}"
) from exc
if expect_file and not resolved.is_file():
raise FileNotFoundError(f"Input file not found: {resolved}")
if create_parent:
@@ -62,5 +59,5 @@ def read_json_file(raw_path: str):
def write_json_file(raw_path: str, payload, *, indent: int = 2, default=None) -> None:
with safe_output_json_path(raw_path).open("w") as fh:
json.dump(payload, fh, indent=indent, default=default)
output_path = safe_output_json_path(raw_path)
output_path.write_text(json.dumps(payload, indent=indent, default=default), encoding="utf-8")
@@ -11,14 +11,6 @@ def _allow_external_paths() -> bool:
return os.getenv("MCD_ALLOW_EXTERNAL_PATHS", "").lower() in {"1", "true", "yes"}
def _is_relative_to(path: Path, root: Path) -> bool:
try:
path.relative_to(root)
return True
except ValueError:
return False
def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_parent: bool = False) -> Path:
value = str(raw_path).strip()
if not value or "\0" in value:
@@ -26,8 +18,13 @@ def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_pare
base = Path.cwd().resolve()
candidate = Path(value).expanduser()
resolved = (candidate if candidate.is_absolute() else base / candidate).resolve()
if not _allow_external_paths() and not _is_relative_to(resolved, base):
raise ValueError(f"Path must stay under the current working directory: {raw_path!r}")
if not _allow_external_paths():
try:
resolved.relative_to(base)
except ValueError as exc:
raise ValueError(
f"Path must stay under the current working directory: {raw_path!r}"
) from exc
if expect_file and not resolved.is_file():
raise FileNotFoundError(f"Input file not found: {resolved}")
if create_parent:
@@ -62,5 +59,5 @@ def read_json_file(raw_path: str):
def write_json_file(raw_path: str, payload, *, indent: int = 2, default=None) -> None:
with safe_output_json_path(raw_path).open("w") as fh:
json.dump(payload, fh, indent=indent, default=default)
output_path = safe_output_json_path(raw_path)
output_path.write_text(json.dumps(payload, indent=indent, default=default), encoding="utf-8")
@@ -11,14 +11,6 @@ def _allow_external_paths() -> bool:
return os.getenv("MCD_ALLOW_EXTERNAL_PATHS", "").lower() in {"1", "true", "yes"}
def _is_relative_to(path: Path, root: Path) -> bool:
try:
path.relative_to(root)
return True
except ValueError:
return False
def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_parent: bool = False) -> Path:
value = str(raw_path).strip()
if not value or "\0" in value:
@@ -26,8 +18,13 @@ def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_pare
base = Path.cwd().resolve()
candidate = Path(value).expanduser()
resolved = (candidate if candidate.is_absolute() else base / candidate).resolve()
if not _allow_external_paths() and not _is_relative_to(resolved, base):
raise ValueError(f"Path must stay under the current working directory: {raw_path!r}")
if not _allow_external_paths():
try:
resolved.relative_to(base)
except ValueError as exc:
raise ValueError(
f"Path must stay under the current working directory: {raw_path!r}"
) from exc
if expect_file and not resolved.is_file():
raise FileNotFoundError(f"Input file not found: {resolved}")
if create_parent:
@@ -62,5 +59,5 @@ def read_json_file(raw_path: str):
def write_json_file(raw_path: str, payload, *, indent: int = 2, default=None) -> None:
with safe_output_json_path(raw_path).open("w") as fh:
json.dump(payload, fh, indent=indent, default=default)
output_path = safe_output_json_path(raw_path)
output_path.write_text(json.dumps(payload, indent=indent, default=default), encoding="utf-8")
@@ -11,14 +11,6 @@ def _allow_external_paths() -> bool:
return os.getenv("MCD_ALLOW_EXTERNAL_PATHS", "").lower() in {"1", "true", "yes"}
def _is_relative_to(path: Path, root: Path) -> bool:
try:
path.relative_to(root)
return True
except ValueError:
return False
def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_parent: bool = False) -> Path:
value = str(raw_path).strip()
if not value or "\0" in value:
@@ -26,8 +18,13 @@ def _resolve_local_path(raw_path: str, *, expect_file: bool = False, create_pare
base = Path.cwd().resolve()
candidate = Path(value).expanduser()
resolved = (candidate if candidate.is_absolute() else base / candidate).resolve()
if not _allow_external_paths() and not _is_relative_to(resolved, base):
raise ValueError(f"Path must stay under the current working directory: {raw_path!r}")
if not _allow_external_paths():
try:
resolved.relative_to(base)
except ValueError as exc:
raise ValueError(
f"Path must stay under the current working directory: {raw_path!r}"
) from exc
if expect_file and not resolved.is_file():
raise FileNotFoundError(f"Input file not found: {resolved}")
if create_parent:
@@ -62,5 +59,5 @@ def read_json_file(raw_path: str):
def write_json_file(raw_path: str, payload, *, indent: int = 2, default=None) -> None:
with safe_output_json_path(raw_path).open("w") as fh:
json.dump(payload, fh, indent=indent, default=default)
output_path = safe_output_json_path(raw_path)
output_path.write_text(json.dumps(payload, indent=indent, default=default), encoding="utf-8")
@@ -12,6 +12,19 @@ import argparse
import re
import sys
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Dict, List, Optional, Tuple, Union
import yaml
@@ -141,8 +154,8 @@ def main() -> None:
args = parser.parse_args()
dbt_project_path = Path(args.dbt_project_path)
model_path = Path(args.model_path)
dbt_project_path = safe_user_path(args.dbt_project_path)
model_path = safe_user_path(args.model_path)
if not dbt_project_path.exists():
print(f"Error: dbt_project.yml not found: {dbt_project_path}", file=sys.stderr)
@@ -2,6 +2,20 @@ import json
import sys
from PIL import Image, ImageDraw
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
# Creates "validation" images with rectangles for the bounding box information that
@@ -35,7 +49,7 @@ if __name__ == "__main__":
print("Usage: create_validation_image.py [page number] [fields.json file] [input image path] [output image path]")
sys.exit(1)
page_number = int(sys.argv[1])
fields_json_path = sys.argv[2]
input_image_path = sys.argv[3]
output_image_path = sys.argv[4]
fields_json_path = safe_user_path(sys.argv[2])
input_image_path = safe_user_path(sys.argv[3])
output_image_path = safe_user_path(sys.argv[4])
create_validation_image(page_number, fields_json_path, input_image_path, output_image_path)
@@ -141,7 +141,7 @@ def write_field_info(pdf_path: str, json_output_path: str):
reader = PdfReader(pdf_path)
field_info = get_field_info(reader)
with open(json_output_path, "w") as f:
json.dump(field_info, f, indent=2)
f.write(json.dumps(field_info, indent=2))
print(f"Wrote {len(field_info)} fields to {json_output_path}")
@@ -13,10 +13,28 @@
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const sanitizeFilename = require('sanitize-filename');
// Change to skill directory for proper module resolution
process.chdir(__dirname);
function safeUserPath(pathValue, baseDir = process.cwd()) {
const root = path.resolve(baseDir);
const segments = String(pathValue ?? '').split(/[\\/]+/).filter(Boolean).map((segment) => {
const sanitized = sanitizeFilename(segment);
if (sanitized !== segment || !sanitized) {
throw new Error(`Unsafe path segment: ${segment}`);
}
return sanitized;
});
const target = path.resolve(root, ...segments);
const rel = path.relative(root, target);
if (rel.startsWith('..') || path.isAbsolute(rel)) {
throw new Error(`Path escapes allowed directory: ${pathValue}`);
}
return target;
}
/**
* Check if Playwright is installed
*/
@@ -54,7 +72,7 @@ function getCodeToExecute() {
// Case 1: File path provided
if (args.length > 0 && fs.existsSync(args[0])) {
const filePath = path.resolve(args[0]);
const filePath = safeUserPath(args[0]);
console.log(`📄 Executing file: ${filePath}`);
return fs.readFileSync(filePath, 'utf8');
}
@@ -16,6 +16,19 @@ import zipfile
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
def validate_input_tree(input_dir: Path):
root = input_dir.resolve(strict=True)
for path in input_dir.rglob("*"):
@@ -27,6 +40,18 @@ def validate_input_tree(input_dir: Path):
raise ValueError(f"Refusing to pack path outside input directory: {path}") from None
def copy_tree_contents(source_dir: Path, target_dir: Path) -> None:
target_dir.mkdir(parents=True, exist_ok=True)
for source_path in source_dir.rglob("*"):
relative_path = source_path.relative_to(source_dir)
target_path = target_dir / relative_path
if source_path.is_dir():
target_path.mkdir(parents=True, exist_ok=True)
elif source_path.is_file():
target_path.parent.mkdir(parents=True, exist_ok=True)
target_path.write_bytes(source_path.read_bytes())
def main():
parser = argparse.ArgumentParser(description="Pack a directory into an Office file")
parser.add_argument("input_directory", help="Unpacked Office document directory")
@@ -65,7 +90,7 @@ def pack_document(input_dir, output_file, validate=False):
bool: True if successful, False if validation failed
"""
input_dir = Path(input_dir)
output_file = Path(output_file)
output_file = safe_user_path(output_file)
if not input_dir.is_dir():
raise ValueError(f"{input_dir} is not a directory")
@@ -76,7 +101,7 @@ def pack_document(input_dir, output_file, validate=False):
# Work in temporary directory to avoid modifying original
with tempfile.TemporaryDirectory() as temp_dir:
temp_content_dir = Path(temp_dir) / "content"
shutil.copytree(input_dir, temp_content_dir)
copy_tree_contents(input_dir, temp_content_dir)
# Process XML files to remove pretty-printing whitespace
for pattern in ["*.xml", "*.rels"]:
@@ -85,10 +110,12 @@ def pack_document(input_dir, output_file, validate=False):
# Create final Office file as zip archive
output_file.parent.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(output_file, "w", zipfile.ZIP_DEFLATED) as zf:
temp_zip_path = Path(temp_dir) / "office.zip"
with zipfile.ZipFile(temp_zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
for f in temp_content_dir.rglob("*"):
if f.is_file():
zf.write(f, f.relative_to(temp_content_dir))
output_file.write_bytes(temp_zip_path.read_bytes())
# Validate if requested
if validate:
@@ -8,6 +8,19 @@ import sys
import zipfile
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
MAX_ARCHIVE_MEMBERS = 5000
MAX_MEMBER_SIZE = 100 * 1024 * 1024
MAX_TOTAL_UNCOMPRESSED = 512 * 1024 * 1024
@@ -30,7 +43,7 @@ def _extract_member(archive: zipfile.ZipFile, member: zipfile.ZipInfo, output_ro
return
destination.parent.mkdir(parents=True, exist_ok=True)
with archive.open(member, "r") as source, open(destination, "wb") as target:
with archive.open(member, "r") as source, safe_user_path(destination).open("wb") as target:
shutil.copyfileobj(source, target)
@@ -57,7 +70,7 @@ def _validate_archive_members(archive: zipfile.ZipFile, output_root: Path):
def extract_archive_safely(input_file: str | Path, output_dir: str | Path):
output_path = Path(output_dir)
output_path = safe_user_path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
output_root = output_path.resolve()
@@ -82,7 +95,7 @@ def main(argv: list[str] | None = None):
raise SystemExit("Usage: python unpack.py <office_file> <output_dir>")
input_file, output_dir = argv
output_path = Path(output_dir)
output_path = safe_user_path(output_dir)
extract_archive_safely(input_file, output_path)
pretty_print_xml(output_path)
@@ -28,6 +28,19 @@ import platform
import sys
from dataclasses import dataclass
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Any, Dict, List, Optional, Tuple, Union
from PIL import Image, ImageDraw, ImageFont
@@ -79,7 +92,7 @@ The output JSON includes:
args = parser.parse_args()
input_path = Path(args.input)
input_path = safe_user_path(args.input)
if not input_path.exists():
print(f"Error: Input file not found: {args.input}")
sys.exit(1)
@@ -96,7 +109,7 @@ The output JSON includes:
)
inventory = extract_text_inventory(input_path, issues_only=args.issues_only)
output_path = Path(args.output)
output_path = safe_user_path(args.output)
output_path.parent.mkdir(parents=True, exist_ok=True)
save_inventory(inventory, output_path)
@@ -1012,8 +1025,8 @@ def save_inventory(inventory: InventoryData, output_path: Path) -> None:
shape_key: shape_data.to_dict() for shape_key, shape_data in shapes.items()
}
with open(output_path, "w", encoding="utf-8") as f:
json.dump(json_inventory, f, indent=2, ensure_ascii=False)
with safe_user_path(output_path).open("w", encoding="utf-8") as f:
f.write(json.dumps(json_inventory, indent=2, ensure_ascii=False))
if __name__ == "__main__":
@@ -15,6 +15,19 @@ import sys
from copy import deepcopy
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
import six
from pptx import Presentation
@@ -53,13 +66,13 @@ Note: Slide indices are 0-based (first slide is 0, second is 1, etc.)
sys.exit(1)
# Check template exists
template_path = Path(args.template)
template_path = safe_user_path(args.template)
if not template_path.exists():
print(f"Error: Template file not found: {args.template}")
sys.exit(1)
# Create output directory if needed
output_path = Path(args.output)
output_path = safe_user_path(args.output)
output_path.parent.mkdir(parents=True, exist_ok=True)
try:
@@ -12,6 +12,19 @@ unless "paragraphs" is specified in the replacements for that shape.
import json
import sys
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Any, Dict, List
from inventory import InventoryData, extract_text_inventory
@@ -359,9 +372,9 @@ def main():
print(__doc__)
sys.exit(1)
input_pptx = Path(sys.argv[1])
replacements_json = Path(sys.argv[2])
output_pptx = Path(sys.argv[3])
input_pptx = safe_user_path(sys.argv[1])
replacements_json = safe_user_path(sys.argv[2])
output_pptx = safe_user_path(sys.argv[3])
if not input_pptx.exists():
print(f"Error: Input file '{input_pptx}' not found")
@@ -8,6 +8,20 @@ import re
from typing import Dict, List, Tuple, Set
from collections import Counter, defaultdict
import json
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
class InterviewAnalyzer:
"""Analyze customer interviews for insights and patterns"""
@@ -424,7 +438,7 @@ def main():
sys.exit(1)
# Read interview transcript
with open(sys.argv[1], 'r') as f:
with safe_user_path(sys.argv[1]).open('r') as f:
interview_text = f.read()
# Analyze
@@ -22,6 +22,19 @@ import sys
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("ckpt_dir", help="Directory containing ablation subdirs (each with best.pth + best_metrics.json)")
@@ -33,7 +46,7 @@ def main() -> int:
"needed only when a checkpoint pickles non-tensor objects (e.g. an args Namespace); OFF by default")
args = ap.parse_args()
root = Path(args.ckpt_dir)
root = safe_user_path(args.ckpt_dir)
if not root.exists():
print(f"ERROR: {root} does not exist")
return 1
@@ -9,13 +9,26 @@ import sys
import json
import argparse
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Dict, List, Optional
class ArchitectureDiagramGenerator:
"""Main class for architecture diagram generator functionality"""
def __init__(self, target_path: str, verbose: bool = False):
self.target_path = Path(target_path)
self.target_path = safe_user_path(target_path)
self.verbose = verbose
self.results = {}
@@ -104,7 +117,7 @@ def main():
if args.json:
output = json.dumps(results, indent=2)
if args.output:
with open(args.output, 'w') as f:
with safe_user_path(args.output).open('w') as f:
f.write(output)
print(f"Results written to {args.output}")
else:
@@ -9,13 +9,26 @@ import sys
import json
import argparse
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Dict, List, Optional
class DependencyAnalyzer:
"""Main class for dependency analyzer functionality"""
def __init__(self, target_path: str, verbose: bool = False):
self.target_path = Path(target_path)
self.target_path = safe_user_path(target_path)
self.verbose = verbose
self.results = {}
@@ -104,7 +117,7 @@ def main():
if args.json:
output = json.dumps(results, indent=2)
if args.output:
with open(args.output, 'w') as f:
with safe_user_path(args.output).open('w') as f:
f.write(output)
print(f"Results written to {args.output}")
else:
@@ -9,13 +9,26 @@ import sys
import json
import argparse
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Dict, List, Optional
class ProjectArchitect:
"""Main class for project architect functionality"""
def __init__(self, target_path: str, verbose: bool = False):
self.target_path = Path(target_path)
self.target_path = safe_user_path(target_path)
self.verbose = verbose
self.results = {}
@@ -104,7 +117,7 @@ def main():
if args.json:
output = json.dumps(results, indent=2)
if args.output:
with open(args.output, 'w') as f:
with safe_user_path(args.output).open('w') as f:
f.write(output)
print(f"Results written to {args.output}")
else:
@@ -17,6 +17,19 @@ import os
import re
import sys
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Dict, List, Optional, Any, Tuple
@@ -375,7 +388,7 @@ def main():
)
args = parser.parse_args()
project_dir = Path(args.project_dir).resolve()
project_dir = safe_user_path(args.project_dir).resolve()
if not project_dir.exists():
print(f"Error: Directory not found: {project_dir}", file=sys.stderr)
@@ -16,6 +16,19 @@ import json
import os
import sys
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Dict, List, Optional
@@ -989,7 +1002,7 @@ def main():
result = scaffold_project(
name=args.name,
output_dir=Path(args.dir),
output_dir=safe_user_path(args.dir),
template=args.template,
features=features,
dry_run=args.dry_run,
@@ -9,13 +9,26 @@ import sys
import json
import argparse
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Dict, List, Optional
class CodeQualityAnalyzer:
"""Main class for code quality analyzer functionality"""
def __init__(self, target_path: str, verbose: bool = False):
self.target_path = Path(target_path)
self.target_path = safe_user_path(target_path)
self.verbose = verbose
self.results = {}
@@ -104,7 +117,7 @@ def main():
if args.json:
output = json.dumps(results, indent=2)
if args.output:
with open(args.output, 'w') as f:
with safe_user_path(args.output).open('w') as f:
f.write(output)
print(f"Results written to {args.output}")
else:
@@ -9,13 +9,26 @@ import sys
import json
import argparse
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Dict, List, Optional
class FullstackScaffolder:
"""Main class for fullstack scaffolder functionality"""
def __init__(self, target_path: str, verbose: bool = False):
self.target_path = Path(target_path)
self.target_path = safe_user_path(target_path)
self.verbose = verbose
self.results = {}
@@ -104,7 +117,7 @@ def main():
if args.json:
output = json.dumps(results, indent=2)
if args.output:
with open(args.output, 'w') as f:
with safe_user_path(args.output).open('w') as f:
f.write(output)
print(f"Results written to {args.output}")
else:
@@ -9,13 +9,26 @@ import sys
import json
import argparse
from pathlib import Path
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
from typing import Dict, List, Optional
class ProjectScaffolder:
"""Main class for project scaffolder functionality"""
def __init__(self, target_path: str, verbose: bool = False):
self.target_path = Path(target_path)
self.target_path = safe_user_path(target_path)
self.verbose = verbose
self.results = {}
@@ -104,7 +117,7 @@ def main():
if args.json:
output = json.dumps(results, indent=2)
if args.output:
with open(args.output, 'w') as f:
with safe_user_path(args.output).open('w') as f:
f.write(output)
print(f"Results written to {args.output}")
else:
@@ -33,6 +33,39 @@ import re
from pathlib import Path
from datetime import datetime
def safe_user_path(path_value, base_dir="."):
"""Resolve a CLI path under the current workspace."""
if base_dir != ".":
raise ValueError("Custom base directories are not supported for CLI paths")
base_path = Path.cwd().resolve()
resolved_path = Path(path_value).expanduser().resolve()
try:
resolved_path.relative_to(base_path)
except ValueError as exc:
raise ValueError(f"Path escapes allowed directory: {path_value}") from exc
return resolved_path
def copy_tree_contents(source_dir: Path, target_dir: Path, *, ignore=None) -> None:
ignored_by_dir = {}
if ignore is not None:
for current_dir in [source_dir, *[p for p in source_dir.rglob("*") if p.is_dir()]]:
ignored_by_dir[current_dir] = set(ignore(str(current_dir), [p.name for p in current_dir.iterdir()]))
target_dir.mkdir(parents=True, exist_ok=True)
for source_path in source_dir.rglob("*"):
ignored_names = ignored_by_dir.get(source_path.parent, set())
if source_path.name in ignored_names:
continue
relative_path = source_path.relative_to(source_dir)
target_path = target_dir / relative_path
if source_path.is_dir():
target_path.mkdir(parents=True, exist_ok=True)
elif source_path.is_file():
target_path.parent.mkdir(parents=True, exist_ok=True)
target_path.write_bytes(source_path.read_bytes())
# Add scripts directory to path for imports
SCRIPT_DIR = Path(__file__).parent.resolve()
sys.path.insert(0, str(SCRIPT_DIR))
@@ -147,7 +180,7 @@ def safe_skill_path(root: Path, skill_name: str) -> Path:
def resolve_skill_source(source: str) -> Path:
"""Resolve and validate a local skill source directory."""
source_path = Path(source).expanduser().resolve()
source_path = safe_user_path(source).expanduser().resolve()
if not source_path.is_dir():
raise ValueError(f"Source does not exist or is not a directory: {source_path}")
if not (source_path / "SKILL.md").is_file():
@@ -164,7 +197,7 @@ def md5_dir(path: Path, exclude_dirs: set = None) -> str:
if exclude_dirs is None:
exclude_dirs = {"backups", "staging", ".git", "__pycache__", "node_modules", ".venv"}
root_path = Path(path).resolve(strict=True)
root_path = safe_user_path(path).resolve(strict=True)
if not root_path.is_dir():
raise ValueError(f"Hash target must be a directory: {root_path}")
@@ -173,7 +206,7 @@ def md5_dir(path: Path, exclude_dirs: set = None) -> str:
# Filter out excluded directories
dirs[:] = [d for d in dirs if d not in exclude_dirs]
for f in sorted(files):
fp = Path(root) / f
fp = safe_user_path(root) / f
try:
resolved_fp = fp.resolve(strict=True)
resolved_fp.relative_to(root_path)
@@ -370,7 +403,7 @@ def step4_check_conflicts(skill_name: str) -> dict:
def _backup_ignore(directory, contents):
"""Ignore function for shutil.copytree to skip backup/staging dirs."""
ignored = set()
dir_path = Path(directory)
dir_path = safe_user_path(directory)
for item in contents:
item_path = dir_path / item
if item_path.is_symlink():
@@ -436,7 +469,7 @@ def step6_copy_to_skills_root(source_path: Path, skill_name: str) -> dict:
# Copy to staging first (skip backups/staging to prevent recursion)
try:
shutil.copytree(source_path, staging, ignore=_backup_ignore, dirs_exist_ok=True)
copy_tree_contents(source_path, staging, ignore=_backup_ignore)
except Exception as e:
return {"success": False, "error": f"Copy to staging failed: {e}"}
@@ -470,7 +503,7 @@ def step6_copy_to_skills_root(source_path: Path, skill_name: str) -> dict:
except Exception as e:
# Try copy + delete as fallback (cross-device moves)
try:
shutil.copytree(staging, dest, dirs_exist_ok=True)
copy_tree_contents(staging, dest)
shutil.rmtree(staging, ignore_errors=True)
except Exception as e2:
shutil.rmtree(staging, ignore_errors=True)
@@ -496,7 +529,7 @@ def step7_register_claude(skill_name: str) -> dict:
# Copy SKILL.md
try:
shutil.copy2(source_skill_md, claude_dest_dir / "SKILL.md")
(claude_dest_dir / "SKILL.md").write_bytes(source_skill_md.read_bytes())
except Exception as e:
return {"success": False, "error": f"Failed to copy SKILL.md to Claude skills: {e}"}
@@ -507,7 +540,7 @@ def step7_register_claude(skill_name: str) -> dict:
try:
if claude_refs.exists():
shutil.rmtree(claude_refs)
shutil.copytree(refs_dir, claude_refs)
copy_tree_contents(refs_dir, claude_refs)
except Exception:
pass # Non-critical

Some files were not shown because too many files have changed in this diff Show More