agentic-awesome-skills 14.2.0 → 14.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
- <!-- registry-sync: version=14.2.0; skills=1946; stars=42926; updated_at=2026-07-12T05:22:55+00:00 -->
1
+ <!-- registry-sync: version=14.3.0; skills=1948; stars=43042; updated_at=2026-07-13T07:58:11+00:00 -->
2
2
  [![Agentic Awesome Skills social preview](apps/web-app/public/social-card.png)](https://github.com/sickn33/agentic-awesome-skills)
3
3
 
4
- # 🌌 Agentic Awesome Skills: 1,946+ Agentic Skills for Claude Code, Gemini CLI, Cursor, Autohand Code, Copilot & More
4
+ # 🌌 Agentic Awesome Skills: 1,948+ Agentic Skills for Claude Code, Gemini CLI, Cursor, Autohand Code, Copilot & More
5
5
 
6
- > **Installable GitHub library of 1,946+ agentic skills for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants.**
6
+ > **Installable GitHub library of 1,948+ agentic skills for Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, and other AI coding assistants.**
7
7
 
8
8
  Agentic 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.
9
9
 
@@ -13,7 +13,7 @@ You can use this repo to install a broad multi-tool skill library, start from fo
13
13
 
14
14
  The canonical project page is the GitHub repository at <https://github.com/sickn33/agentic-awesome-skills>; the hosted catalog is a companion discovery surface for search, plugins, and skill detail pages.
15
15
 
16
- **Start here:** [Install in 1 minute](#installation) · [Recommended plugins](#recommended-specialized-plugins) · [Compare plugin packs](https://sickn33.github.io/agentic-awesome-skills/plugins) · [Choose your tool](#choose-your-tool) · [📚 Browse 1,946+ Skills](#browse-1946-skills) · [Bundles & workflows](#bundles--workflows) · [Support the project](#support-the-project)
16
+ **Start here:** [Install in 1 minute](#installation) · [Recommended plugins](#recommended-specialized-plugins) · [Compare plugin packs](https://sickn33.github.io/agentic-awesome-skills/plugins) · [Choose your tool](#choose-your-tool) · [📚 Browse 1,948+ Skills](#browse-1948-skills) · [Bundles & workflows](#bundles--workflows) · [Support the project](#support-the-project)
17
17
 
18
18
  [![GitHub stars](https://img.shields.io/badge/⭐%2043%2C000%2B%20Stars-gold?style=for-the-badge)](https://github.com/sickn33/agentic-awesome-skills/stargazers)
19
19
  [![Follow @AASkills_ on X](https://img.shields.io/badge/Follow-%40AASkills__-black?style=for-the-badge&logo=x)](https://x.com/AASkills_)
@@ -30,13 +30,13 @@ The canonical project page is the GitHub repository at <https://github.com/sickn
30
30
  [![OpenCode](https://img.shields.io/badge/OpenCode-CLI-gray?style=for-the-badge)](https://github.com/opencode-ai/opencode)
31
31
  [![Antigravity](https://img.shields.io/badge/Antigravity-AI%20IDE-red?style=for-the-badge)](https://github.com/sickn33/agentic-awesome-skills)
32
32
 
33
- **Current release: V14.2.0.** Trusted by 43k+ 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.
33
+ **Current release: V14.3.0.** Trusted by 43k+ 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.
34
34
 
35
35
  ## Why This Repo
36
36
 
37
37
  - **Installable, not just inspirational**: use `npx agentic-awesome-skills` to put skills where your tool expects them.
38
38
  - **Built for major agent workflows**: Claude Code, Cursor, Codex CLI, Autohand Code, Gemini CLI, Antigravity, Kiro, OpenCode, Copilot, and more.
39
- - **Broad coverage with real utility**: 1,946+ skills across development, testing, security, infrastructure, product, and marketing.
39
+ - **Broad coverage with real utility**: 1,948+ skills across development, testing, security, infrastructure, product, and marketing.
40
40
  - **Focused by default**: specialized plugins help you start with the web, security, data, docs, DevOps, QA, OSS, or agent/MCP workflows you actually need.
41
41
  - **Useful whether you want breadth or curation**: install the full catalog, choose a specialized plugin, start with bundles, or compare alternatives before installing.
42
42
 
@@ -48,7 +48,7 @@ The canonical project page is the GitHub repository at <https://github.com/sickn
48
48
  - [Choose Your Tool](#choose-your-tool)
49
49
  - [Quick FAQ](#quick-faq)
50
50
  - [Bundles & Workflows](#bundles--workflows)
51
- - [Browse 1,946+ Skills](#browse-1946-skills)
51
+ - [Browse 1,948+ Skills](#browse-1948-skills)
52
52
  - [Troubleshooting](#troubleshooting)
53
53
  - [Stable Skills Manifest v1](#stable-skills-manifest-v1)
54
54
  - [Support the Project](#support-the-project)
@@ -79,6 +79,18 @@ npx agentic-awesome-skills --agy
79
79
 
80
80
  The npm installer uses a shallow, release-pinned clone by default so first-run installs stay lighter than a full repository history checkout while matching the published npm package version. Use `--tag main` only when you intentionally want the current repository tip.
81
81
 
82
+ ### Focused single-skill install with GitHub CLI (preview)
83
+
84
+ GitHub CLI can preview and install one exact skill for Copilot and other supported hosts. Use an exact `SKILL.md` path in this large, mirrored repository so the selected source is unambiguous and discovery stays fast:
85
+
86
+ ```bash
87
+ gh skill preview sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md
88
+ gh skill install sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md \
89
+ --agent github-copilot --scope user --pin v14.2.0
90
+ ```
91
+
92
+ `gh skill` support is currently a GitHub CLI preview and may change. Install a focused skill or plugin surface for the job; do not use `--all` unless you intentionally want every discovered canonical and mirrored skill.
93
+
82
94
  ### Verify the install
83
95
 
84
96
  ```bash
@@ -142,7 +154,7 @@ Use the same repository, but install or invoke it in the way your host expects.
142
154
  | Antigravity CLI (`agy`) | `npx agentic-awesome-skills --agy` | `/brainstorming help me plan a feature` |
143
155
  | Kiro CLI | `npx agentic-awesome-skills --kiro` | `Use brainstorming to plan a feature` |
144
156
  | Kiro IDE | `npx agentic-awesome-skills --path ~/.kiro/skills` | `Use @brainstorming to plan a feature` |
145
- | GitHub Copilot | _No installer — paste skills or rules manually_ | `Ask Copilot to use brainstorming to plan a feature` |
157
+ | GitHub Copilot | `gh skill install sickn33/agentic-awesome-skills skills/brainstorming/SKILL.md --agent github-copilot --scope user --pin v14.2.0` (preview) | `Ask Copilot to use brainstorming to plan a feature` |
146
158
  | OpenCode | `npx agentic-awesome-skills --path .agents/skills --category development,backend --risk safe,none` | `opencode run @brainstorming help me plan a feature` |
147
159
  | AdaL CLI | `npx agentic-awesome-skills --path .adal/skills` | `Use brainstorming to plan a feature` |
148
160
  | Custom path | `npx agentic-awesome-skills --path ./my-skills` | Depends on your tool |
@@ -159,7 +171,7 @@ Use the table above for install targets. Use specialized plugins when you are ch
159
171
 
160
172
  ### What is Agentic Awesome Skills?
161
173
 
162
- **Agentic Awesome Skills** (Release 14.2.0) is a large, installable skill library for AI coding assistants. It packages 1,946+ 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.
174
+ **Agentic Awesome Skills** (Release 14.3.0) is a large, installable skill library for AI coding assistants. It packages 1,948+ 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.
163
175
 
164
176
  ### How do I install it?
165
177
 
@@ -224,7 +236,15 @@ If Antigravity starts hitting context limits with too many active skills, the ac
224
236
 
225
237
  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.
226
238
 
227
- ## Browse 1,946+ Skills
239
+ For a reproducible exact set, pin the package and catalog release and preview the full per-target plan before writing:
240
+
241
+ ```bash
242
+ npx agentic-awesome-skills@14.3.0 --codex --release 14.3.0 --skills frontend-design,game-development/2d-games --dry-run
243
+ ```
244
+
245
+ Remove `--dry-run` only after reviewing the install, update, and removal plan. Unknown or ambiguous skill identifiers fail closed, and metadata filters combine with `--skills` using AND.
246
+
247
+ ## Browse 1,948+ Skills
228
248
 
229
249
  Use the root repo as a landing page, then jump into the deeper surface that matches your intent.
230
250
 
@@ -398,6 +418,7 @@ Key source families include:
398
418
  - **[adelaidasofia/ai-brain-starter](https://github.com/adelaidasofia/ai-brain-starter)**: Source for the `ingest-youtube` skill - YouTube transcript ingestion into markdown vaults with yt-dlp metadata, VTT cleanup, and capture-seed stubs (MIT).
399
419
  - **[JDDavenport/context-kit](https://github.com/JDDavenport/context-kit)**: Source reference for the `context-kit` skill - local-first Personal Context Artifact setup, installer review, and private context hygiene for Claude Code and adjacent agent workflows.
400
420
  - **[JularDepick/user-thoughts.SKILL](https://github.com/JularDepick/user-thoughts.SKILL)**: Source for the `user-thoughts` skill - persistent project idea repository workflows for capturing decisions, tech stack notes, UI/UX rationale, and MDBASE-backed project memory (MIT).
421
+ - **[TheaDust/lore](https://github.com/TheaDust/lore)**: Source for the `lore` skill - Markdown-only, zero-dependency long-term project memory for AI coding agents, with monorepo scopes, two-section platform mirrors, and stdlib Python helpers (MIT).
401
422
  - **[ZeroPointRepo/youtube-skills](https://github.com/ZeroPointRepo/youtube-skills)**: Source for the `youtube-full` skill - TranscriptAPI-backed YouTube transcripts, search, channel browsing, playlists, and cloud-safe video research workflows (MIT).
402
423
  - **[ejentum/ejentum-mcp](https://github.com/ejentum/ejentum-mcp)**: Source for the `ejentum-reasoning-harness` skill - MCP cognitive harness modes for reasoning, code review, anti-deception checks, and memory-drift analysis (MIT).
403
424
  - **[luoyuctl/agenttrace](https://github.com/luoyuctl/agenttrace)**: Source for the `agenttrace-session-audit` skill - local AI coding-agent session audits for cost spikes, tool failures, latency gaps, anomalies, health gates, and session diffs (MIT).
@@ -423,6 +444,7 @@ Key source families include:
423
444
  - **[Elkidogz/technical-change-skill](https://github.com/Elkidogz/technical-change-skill)**: Source for the Technical Change Tracker skill - structured JSON change records, session handoff, and accessible HTML dashboards for coding continuity.
424
445
  - **[vipin-si/article-illustrations](https://github.com/vipin-si/article-illustrations)**: Source for the `article-illustrations` skill - Grav-style hand-drawn article illustrations with whiteboard sketches, sparse annotations, and visual metaphor QA guidance (MIT).
425
446
  - **[274326424/video-content-extractor](https://github.com/274326424/video-content-extractor)**: Source for the `video-content-extractor` skill - FFmpeg and Tesseract OCR workflows for extracting timestamped screen text and structured Markdown reports from MP4 videos (MIT).
447
+ - **[metrox-eth/quit-sponsor](https://github.com/metrox-eth/quit-sponsor)**: Source for the `quit-sponsor` skill - evidence-based quit-smoking sponsorship for agents with persistent memory: 44-source cited protocols, sponsor decision tree, three-clause contract, wave protocol, slip attribution coaching, and a timestamped logbook (MIT).
426
448
  - **[rmyndharis/antigravity-skills](https://github.com/rmyndharis/antigravity-skills)**: For the massive contribution of 300+ Enterprise skills and the catalog generation logic.
427
449
  - **[amartelr/antigravity-workspace-manager](https://github.com/amartelr/antigravity-workspace-manager)**: Workspace Manager CLI companion to dynamically auto-provision subsets of skills across local development environments.
428
450
  - **[obra/superpowers](https://github.com/obra/superpowers)**: The original "Superpowers" by Jesse Vincent.
@@ -482,7 +504,7 @@ Key source families include:
482
504
  - **[christopherlhammer11-ai/tool-use-guardian](https://github.com/christopherlhammer11-ai/tool-use-guardian)**: Source for the Tool Use Guardian skill — tool-call reliability wrapper with retries, recovery, and failure classification.
483
505
  - **[christopherlhammer11-ai/recallmax](https://github.com/christopherlhammer11-ai/recallmax)**: Source for the RecallMax skill — long-context memory, summarization, and conversation compression for agents.
484
506
  - **[tsilverberg/webapp-uat](https://github.com/tsilverberg/webapp-uat)**: Full browser UAT skill — Playwright testing with console/network error capture, WCAG 2.2 AA accessibility checks, i18n validation, responsive testing, and P0-P3 bug triage. Read-only by default, works with React, Vue, Angular, Ionic, Next.js.
485
- - **[Wolfe-Jam/faf-skills](https://github.com/Wolfe-Jam/faf-skills)**: AI-context and project DNA skills — .faf format management, AI-readiness scoring, bi-sync, MCP server building, and championship-grade testing (17 skills, MIT).
507
+ - **[Wolfe-Jam/faf-skills](https://github.com/Wolfe-Jam/faf-skills)**: AI-context and project DNA skills — .faf format management, AI-readiness scoring, bi-sync, MCP server building, and championship-grade testing (7 skills, MIT).
486
508
  - **[fullstackcrew-alpha/privacy-mask](https://github.com/fullstackcrew-alpha/privacy-mask)**: Local image privacy masking for AI coding agents. Detects and redacts PII, API keys, and secrets in screenshots via OCR + 47 regex rules. Claude Code hook integration for automatic masking. Supports Tesseract and RapidOCR. 100% offline (MIT).
487
509
  - **[AvdLee/SwiftUI-Agent-Skill](https://github.com/AvdLee/SwiftUI-Agent-Skill)**: SwiftUI best-practices skill for agent workflows (MIT).
488
510
  - **[CloudAI-X/threejs-skills](https://github.com/CloudAI-X/threejs-skills)**: Three.js-focused skill collection for agent-assisted 3D web work.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agentic-awesome-skills",
3
- "version": "14.2.0",
4
- "description": "1,946+ agentic skills for Claude Code, Gemini CLI, Cursor, Antigravity & more. Installer CLI.",
3
+ "version": "14.3.0",
4
+ "description": "1,948+ agentic skills for Claude Code, Gemini CLI, Cursor, Antigravity & more. Installer CLI.",
5
5
  "license": "MIT",
6
6
  "scripts": {
7
7
  "validate": "node tools/scripts/run-python.js tools/scripts/validate_skills.py",
@@ -34,6 +34,9 @@
34
34
  "build": "npm run chain && npm run catalog",
35
35
  "check:stale-claims": "node tools/scripts/run-python.js tools/scripts/check_stale_claims.py",
36
36
  "check:live-seo": "node tools/scripts/check-live-seo-geo.js",
37
+ "pages:redirect-bridge": "node tools/scripts/generate-pages-redirect-bridge.js",
38
+ "traffic:normalize": "node tools/scripts/normalize_traffic_snapshots.js",
39
+ "traffic:migration-readiness": "node tools/scripts/audit_search_migration_readiness.js",
37
40
  "check:warning-budget": "node tools/scripts/run-python.js tools/scripts/check_validation_warning_budget.py",
38
41
  "check:readme-credits": "node tools/scripts/run-python.js tools/scripts/check_readme_credits.py",
39
42
  "audit:consistency": "node tools/scripts/run-python.js tools/scripts/audit_consistency.py",
@@ -41,7 +41,9 @@ function parseArgs(argv = process.argv.slice(2)) {
41
41
  let riskArg = null;
42
42
  let categoryArg = null;
43
43
  let tagsArg = null;
44
+ let skillsArg = null;
44
45
  let versionInfo = false;
46
+ let dryRun = false;
45
47
  let cursor = false,
46
48
  claude = false,
47
49
  gemini = false,
@@ -56,7 +58,7 @@ function parseArgs(argv = process.argv.slice(2)) {
56
58
  versionInfo = true;
57
59
  continue;
58
60
  }
59
- if (["--path", "--release", "--tag", "--risk", "--category", "--tags"].includes(a[i])) {
61
+ if (["--path", "--release", "--tag", "--risk", "--category", "--tags", "--skills"].includes(a[i])) {
60
62
  const value = a[i + 1];
61
63
  if (!value || value.startsWith("--")) {
62
64
  throw new Error(`Option ${a[i]} requires a value.`);
@@ -67,9 +69,14 @@ function parseArgs(argv = process.argv.slice(2)) {
67
69
  if (a[i] === "--risk") riskArg = value;
68
70
  if (a[i] === "--category") categoryArg = value;
69
71
  if (a[i] === "--tags") tagsArg = value;
72
+ if (a[i] === "--skills") skillsArg = value;
70
73
  i += 1;
71
74
  continue;
72
75
  }
76
+ if (a[i] === "--dry-run") {
77
+ dryRun = true;
78
+ continue;
79
+ }
73
80
  if (a[i] === "--cursor") {
74
81
  cursor = true;
75
82
  continue;
@@ -109,7 +116,9 @@ function parseArgs(argv = process.argv.slice(2)) {
109
116
  riskArg,
110
117
  categoryArg,
111
118
  tagsArg,
119
+ skillsArg,
112
120
  versionInfo,
121
+ dryRun,
113
122
  cursor,
114
123
  claude,
115
124
  gemini,
@@ -179,6 +188,8 @@ Options:
179
188
  --risk <csv> Install only skills matching these risk labels
180
189
  --category <csv> Install only skills matching these categories
181
190
  --tags <csv> Install only skills matching these tags
191
+ --skills <csv> Set exact managed skill names, ids, or nested skill paths
192
+ --dry-run Preview installs/updates/removals for every target without writing
182
193
  --version Print the installer version
183
194
  --release <ver> Clone tag v<ver> (e.g. 4.6.0 -> v4.6.0)
184
195
  --tag <tag> Clone this tag or branch (e.g. v4.6.0, main)
@@ -191,6 +202,7 @@ Examples:
191
202
  npx agentic-awesome-skills --agy
192
203
  npx agentic-awesome-skills --path .agents/skills --category development,backend --risk safe,none
193
204
  npx agentic-awesome-skills --path .agents/skills --tags debugging,typescript-legacy-
205
+ npx agentic-awesome-skills --codex --skills frontend-design,game-development/2d-games --dry-run
194
206
  npx agentic-awesome-skills --release 4.6.0
195
207
  npx agentic-awesome-skills --path ./my-skills
196
208
  npx agentic-awesome-skills --claude --codex Install to multiple targets
@@ -230,6 +242,19 @@ function parseSelectorArg(raw) {
230
242
  };
231
243
  }
232
244
 
245
+ function parseExactSkillArg(raw) {
246
+ if (typeof raw !== "string" || !raw.trim()) {
247
+ return [];
248
+ }
249
+
250
+ const values = raw.split(",").map((value) => value.trim());
251
+ if (values.some((value) => !value)) {
252
+ throw new Error("--skills must be a comma-separated list of non-empty exact skill names, ids, or paths.");
253
+ }
254
+
255
+ return uniqueValues(values);
256
+ }
257
+
233
258
  function hasActiveSelector(selector) {
234
259
  return selector.include.length > 0 || selector.exclude.length > 0;
235
260
  }
@@ -422,21 +447,51 @@ function replaceManagedEntry(
422
447
  }
423
448
 
424
449
  /** Copy contents of repo's skills/ into target so each skill is target/skill-name/ (for Claude Code etc.). */
425
- function getInstallEntries(tempDir, selectors = buildInstallSelectors({})) {
450
+ function resolveExactSkillSelections(repoSkills, skillEntries, requestedSkills = []) {
451
+ if (requestedSkills.length === 0) {
452
+ return null;
453
+ }
454
+
455
+ const skills = skillEntries.map((skillId) => readSkill(repoSkills, skillId));
456
+ const resolvedEntries = new Set();
457
+
458
+ for (const requestedSkill of requestedSkills) {
459
+ const matches = skills.filter((skill) => (
460
+ skill.name === requestedSkill ||
461
+ skill.id === requestedSkill ||
462
+ path.basename(skill.id) === requestedSkill
463
+ ));
464
+
465
+ if (matches.length === 0) {
466
+ throw new Error(`Unknown skill requested by --skills: ${requestedSkill}`);
467
+ }
468
+ if (matches.length > 1) {
469
+ throw new Error(
470
+ `Ambiguous skill requested by --skills: ${requestedSkill}. Use one exact nested skill path: ${matches.map((skill) => skill.id).join(", ")}`,
471
+ );
472
+ }
473
+
474
+ resolvedEntries.add(matches[0].id);
475
+ }
476
+
477
+ return resolvedEntries;
478
+ }
479
+
480
+ function getInstallEntries(tempDir, selectors = buildInstallSelectors({}), requestedSkills = []) {
426
481
  const repoSkills = path.join(tempDir, "skills");
427
482
  if (!fs.existsSync(repoSkills)) {
428
- console.error("Cloned repo has no skills/ directory.");
429
- process.exit(1);
483
+ throw new Error("Cloned repo has no skills/ directory.");
430
484
  }
431
485
 
432
486
  const skillEntries = listSkillIdsRecursive(repoSkills);
433
- const filteredEntries = hasInstallSelectors(selectors)
434
- ? skillEntries.filter((skillId) => matchesInstallSelectors(readSkill(repoSkills, skillId), selectors))
435
- : skillEntries;
436
-
437
- if (hasInstallSelectors(selectors) && filteredEntries.length === 0) {
438
- console.error("No skills matched the requested --risk/--category/--tags filters.");
439
- process.exit(1);
487
+ const selectedEntries = resolveExactSkillSelections(repoSkills, skillEntries, requestedSkills);
488
+ const filteredEntries = skillEntries.filter((skillId) => (
489
+ (!selectedEntries || selectedEntries.has(skillId)) &&
490
+ (!hasInstallSelectors(selectors) || matchesInstallSelectors(readSkill(repoSkills, skillId), selectors))
491
+ ));
492
+
493
+ if ((selectedEntries || hasInstallSelectors(selectors)) && filteredEntries.length === 0) {
494
+ throw new Error("No skills matched the requested --skills/--risk/--category/--tags selection.");
440
495
  }
441
496
 
442
497
  const entries = [...filteredEntries];
@@ -550,6 +605,22 @@ function readInstallManifest(targetPath) {
550
605
  }
551
606
  }
552
607
 
608
+ function normalizeManifestEntries(entries) {
609
+ const normalized = [];
610
+ const invalid = [];
611
+ for (const entry of entries) {
612
+ try {
613
+ normalized.push(normalizeInstallEntry(entry));
614
+ } catch (error) {
615
+ invalid.push(entry);
616
+ }
617
+ }
618
+ return {
619
+ entries: uniqueValues(normalized).sort(),
620
+ invalid: uniqueValues(invalid).sort(),
621
+ };
622
+ }
623
+
553
624
  function writeInstallManifest(targetPath, installEntries) {
554
625
  const manifestPath = resolveInstallManifestPath(targetPath);
555
626
  const normalizedEntries = [...new Set(installEntries.map(normalizeInstallEntry).filter(Boolean))].sort();
@@ -572,14 +643,17 @@ function writeInstallManifest(targetPath, installEntries) {
572
643
 
573
644
  function pruneRemovedEntries(targetPath, previousEntries, installEntries) {
574
645
  const next = new Set(installEntries.map(normalizeInstallEntry));
575
- for (const entry of previousEntries) {
576
- const normalizedEntry = normalizeInstallEntry(entry);
646
+ const normalizedPrevious = normalizeManifestEntries(previousEntries);
647
+ for (const entry of normalizedPrevious.invalid) {
648
+ console.warn(` Skipping unsafe managed entry path from manifest: ${entry}`);
649
+ }
650
+ for (const normalizedEntry of normalizedPrevious.entries) {
577
651
  if (next.has(normalizedEntry)) {
578
652
  continue;
579
653
  }
580
- const candidate = resolveManagedPath(targetPath, entry);
654
+ const candidate = resolveManagedPath(targetPath, normalizedEntry);
581
655
  if (!candidate) {
582
- console.warn(` Skipping unsafe managed entry path from manifest: ${entry}`);
656
+ console.warn(` Skipping unsafe managed entry path from manifest: ${normalizedEntry}`);
583
657
  continue;
584
658
  }
585
659
  assertSafeDestinationPath(candidate, targetPath);
@@ -658,7 +732,16 @@ function resolveInstallRef(opts) {
658
732
  return DEFAULT_RELEASE_REF;
659
733
  }
660
734
 
661
- function installForTarget(tempDir, target, selectors = buildInstallSelectors({})) {
735
+ function installForTarget(
736
+ tempDir,
737
+ target,
738
+ selectors = buildInstallSelectors({}),
739
+ installEntries = null,
740
+ requestedSkills = [],
741
+ ) {
742
+ // Resolve all selection errors before creating or changing the target.
743
+ const resolvedInstallEntries = installEntries || getInstallEntries(tempDir, selectors, requestedSkills);
744
+
662
745
  if (fs.existsSync(target.path)) {
663
746
  ensureTargetIsDirectory(target.path);
664
747
  const targetStats = fs.lstatSync(target.path);
@@ -698,10 +781,9 @@ function installForTarget(tempDir, target, selectors = buildInstallSelectors({})
698
781
  fs.mkdirSync(target.path, { recursive: true });
699
782
  }
700
783
 
701
- const installEntries = getInstallEntries(tempDir, selectors);
702
- const managedEntries = getManagedEntries(installEntries, target);
784
+ const managedEntries = getManagedEntries(resolvedInstallEntries, target);
703
785
  const previousEntries = readInstallManifest(target.path);
704
- installSkillsIntoTarget(tempDir, target.path, installEntries);
786
+ installSkillsIntoTarget(tempDir, target.path, resolvedInstallEntries);
705
787
  pruneRemovedEntries(target.path, previousEntries, managedEntries);
706
788
  writeInstallManifest(target.path, managedEntries);
707
789
  console.log(` ✓ Installed to ${target.path}`);
@@ -737,7 +819,7 @@ function getPostInstallMessages(targets, selectors = buildInstallSelectors({}))
737
819
 
738
820
  if (targets.some((target) => isOpenCodeStylePath(target.path))) {
739
821
  const baseMessage =
740
- "For Antigravity 2.0, OpenCode, or other .agents/skills installs, prefer a reduced install with --risk, --category, or --tags to avoid context overload.";
822
+ "For Antigravity 2.0, OpenCode, or other .agents/skills installs, prefer a reduced install with --skills, --risk, --category, or --tags to avoid context overload.";
741
823
  messages.push(baseMessage);
742
824
  if (!hasInstallSelectors(selectors)) {
743
825
  messages.push(
@@ -749,6 +831,82 @@ function getPostInstallMessages(targets, selectors = buildInstallSelectors({}))
749
831
  return messages;
750
832
  }
751
833
 
834
+ function buildDryRunTargetPlan(target, installEntries) {
835
+ const desiredEntries = uniqueValues(getManagedEntries(installEntries, target)).sort();
836
+ const targetExists = fs.existsSync(target.path);
837
+
838
+ if (targetExists) {
839
+ const stats = fs.lstatSync(target.path);
840
+ if (stats.isSymbolicLink()) {
841
+ throw new Error(`Refusing to preview through symlinked target: ${target.path}`);
842
+ }
843
+ if (!stats.isDirectory()) {
844
+ throw new Error(`Install path exists but is not a directory: ${target.path}`);
845
+ }
846
+ }
847
+
848
+ const previous = normalizeManifestEntries(readInstallManifest(target.path));
849
+ const desiredSet = new Set(desiredEntries);
850
+ const remove = previous.entries.filter((entry) => !desiredSet.has(entry));
851
+
852
+ // Match the apply-time destination checks without creating any path. This
853
+ // makes the preview fail before a later install could encounter a symlinked
854
+ // managed destination or an unsafe stale manifest entry.
855
+ for (const entry of [...desiredEntries, ...remove]) {
856
+ const candidate = resolveManagedPath(target.path, entry);
857
+ if (!candidate) {
858
+ throw new Error(`Refusing unsafe managed entry in dry-run plan: ${entry}`);
859
+ }
860
+ assertSafeDestinationPath(candidate, target.path);
861
+ }
862
+
863
+ return {
864
+ name: target.name,
865
+ path: target.path,
866
+ targetExists,
867
+ replacesRepositoryClone: targetExists && fs.existsSync(path.join(target.path, ".git")),
868
+ installOrUpdate: desiredEntries,
869
+ remove,
870
+ ignoredUnsafeManifestEntries: previous.invalid,
871
+ };
872
+ }
873
+
874
+ function buildDryRunPlan(ref, targets, installEntries) {
875
+ return {
876
+ ref: ref || "default release",
877
+ targets: targets.map((target) => buildDryRunTargetPlan(target, installEntries)),
878
+ skills: installEntries.filter((entry) => entry !== "docs").sort(),
879
+ };
880
+ }
881
+
882
+ function printDryRunPlan(plan) {
883
+ console.log("\nDry run: no target files or directories will be created, changed, or removed.");
884
+ console.log(`Ref: ${plan.ref}`);
885
+ console.log(`Exact skill set (${plan.skills.length}):`);
886
+ for (const skill of plan.skills) {
887
+ console.log(` ${skill}`);
888
+ }
889
+ console.log("Target mutation plans:");
890
+ for (const target of plan.targets) {
891
+ console.log(` ${target.name}: ${target.path}`);
892
+ console.log(` target: ${target.targetExists ? "existing directory" : "will be created"}`);
893
+ if (target.replacesRepositoryClone) {
894
+ console.log(" migration: existing repository clone will be backed up and replaced");
895
+ }
896
+ console.log(` install/update managed entries (${target.installOrUpdate.length}):`);
897
+ for (const entry of target.installOrUpdate) {
898
+ console.log(` ${entry}`);
899
+ }
900
+ console.log(` remove stale managed entries (${target.remove.length}):`);
901
+ for (const entry of target.remove) {
902
+ console.log(` ${entry}`);
903
+ }
904
+ for (const entry of target.ignoredUnsafeManifestEntries) {
905
+ console.log(` ignored unsafe manifest entry: ${entry}`);
906
+ }
907
+ }
908
+ }
909
+
752
910
  function main() {
753
911
  let opts;
754
912
  try {
@@ -759,6 +917,14 @@ function main() {
759
917
  return;
760
918
  }
761
919
  const selectors = buildInstallSelectors(opts);
920
+ let requestedSkills;
921
+ try {
922
+ requestedSkills = parseExactSkillArg(opts.skillsArg);
923
+ } catch (error) {
924
+ console.error(`Error: ${error.message}`);
925
+ process.exitCode = 1;
926
+ return;
927
+ }
762
928
  const ref = resolveInstallRef(opts);
763
929
 
764
930
  if (opts.help) {
@@ -788,10 +954,38 @@ function main() {
788
954
  }
789
955
  run("git", buildCloneArgs(REPO, tempDir, ref));
790
956
 
957
+ // Resolve the exact set once before touching any target. This keeps an
958
+ // unknown/ambiguous --skills value or an empty filter intersection atomic
959
+ // across multi-target installs.
960
+ let installEntries;
961
+ try {
962
+ installEntries = getInstallEntries(tempDir, selectors, requestedSkills);
963
+ } catch (error) {
964
+ console.error(`Error: ${error.message}`);
965
+ process.exitCode = 1;
966
+ return;
967
+ }
968
+
969
+ // Preflight every target before mutating the first one. The same plan is
970
+ // printed for --dry-run and acts as the multi-target safety gate for apply.
971
+ let dryRunPlan;
972
+ try {
973
+ dryRunPlan = buildDryRunPlan(ref, targets, installEntries);
974
+ } catch (error) {
975
+ console.error(`Error: ${error.message}`);
976
+ process.exitCode = 1;
977
+ return;
978
+ }
979
+
980
+ if (opts.dryRun) {
981
+ printDryRunPlan(dryRunPlan);
982
+ return;
983
+ }
984
+
791
985
  console.log(`\nInstalling for ${targets.length} target(s):`);
792
986
  for (const target of targets) {
793
987
  console.log(`\n${target.name}:`);
794
- installForTarget(tempDir, target, selectors);
988
+ installForTarget(tempDir, target, selectors, installEntries);
795
989
  }
796
990
 
797
991
  for (const message of getPostInstallMessages(targets, selectors)) {
@@ -821,6 +1015,8 @@ module.exports = {
821
1015
  replaceManagedEntry,
822
1016
  getPostInstallMessages,
823
1017
  buildCloneArgs,
1018
+ buildDryRunPlan,
1019
+ buildDryRunTargetPlan,
824
1020
  buildInstallSelectors,
825
1021
  getInstallEntries,
826
1022
  getManagedEntries,
@@ -831,10 +1027,14 @@ module.exports = {
831
1027
  main,
832
1028
  matchesInstallSelectors,
833
1029
  normalizeInstallEntry,
1030
+ normalizeManifestEntries,
1031
+ parseExactSkillArg,
834
1032
  parseSelectorArg,
1033
+ printDryRunPlan,
835
1034
  parseArgs,
836
1035
  pruneRemovedEntries,
837
1036
  readInstallManifest,
1037
+ resolveExactSkillSelections,
838
1038
  resolveInstallRef,
839
1039
  writeInstallManifest,
840
1040
  };