@maccesar/aiskills 1.12.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +89 -9
  2. package/lib/cleanup.js +29 -0
  3. package/lib/commands/skills.js +110 -9
  4. package/lib/config.js +18 -9
  5. package/lib/installer.js +5 -3
  6. package/lib/symlink.js +45 -3
  7. package/lib/utils.js +41 -0
  8. package/package.json +2 -2
  9. package/skills/audit-codebase/SKILL.md +70 -0
  10. package/skills/audit-codebase/agents/openai.yaml +4 -0
  11. package/skills/audit-codebase/references/comprehensive-audit.md +220 -0
  12. package/skills/audit-codebase/references/report-format.md +119 -0
  13. package/skills/humaniza/SKILL.md +55 -4
  14. package/skills/humaniza/references/ai-patterns-es.md +40 -0
  15. package/skills/humaniza/references/checklist.md +9 -0
  16. package/skills/humaniza/references/examples.md +16 -0
  17. package/skills/humaniza/references/lexicon-es-mx.md +18 -0
  18. package/skills/humaniza/references/structures-es.md +132 -0
  19. package/skills/humaniza/scripts/check_ai_patterns.py +216 -0
  20. package/skills/refactoring-ui/SKILL.md +65 -29
  21. package/skills/refactoring-ui/references/05-motion.md +124 -0
  22. package/skills/refactoring-ui/references/06-dark-mode.md +117 -0
  23. package/skills/refactoring-ui/references/07-component-patterns.md +181 -0
  24. package/skills/session-log/SKILL.md +524 -0
  25. package/skills/session-log/evals/README.md +79 -0
  26. package/skills/session-log/evals/ab-ronda-1.md +115 -0
  27. package/skills/session-log/evals/ab-ronda-2.md +174 -0
  28. package/skills/session-log/evals/defecto-experimento.md +12 -0
  29. package/skills/session-log/evals/evals.json +147 -0
  30. package/skills/session-log/evals/trigger-eval.json +23 -0
  31. package/skills/session-log/references/file-layout.md +336 -0
  32. package/skills/session-log/references/verification.md +173 -0
  33. package/skills/stitch-showcase/SKILL.md +24 -232
  34. package/skills/stitch-showcase/references/07-theme-system.md +12 -0
  35. package/skills/stitch-showcase/references/08-type-detection.md +9 -1
  36. package/skills/stitch-showcase/references/10-component-standardization.md +25 -0
  37. package/skills/stitch-showcase/references/12-video-embedding.md +113 -0
  38. package/skills/stitch-showcase/references/13-language-detection.md +82 -0
  39. package/skills/stitch-showcase/references/14-troubleshooting-known-issues.md +122 -0
  40. package/skills/stitch-showcase/references/15-build-flags.md +71 -0
  41. package/skills/stitch-showcase/references/16-design-md-format.md +107 -0
  42. package/skills/stitch-showcase/references/index.html +25 -19
  43. package/skills/stitch-showcase/references/viewer.html +24 -12
  44. package/skills/stitch-showcase/scripts/__pycache__/build_showcase.cpython-314.pyc +0 -0
  45. package/skills/stitch-showcase/scripts/__pycache__/component_utils.cpython-314.pyc +0 -0
  46. package/skills/stitch-showcase/scripts/__pycache__/detect_components.cpython-314.pyc +0 -0
  47. package/skills/stitch-showcase/scripts/__pycache__/extract_catalog.cpython-314.pyc +0 -0
  48. package/skills/stitch-showcase/scripts/__pycache__/extract_text.cpython-314.pyc +0 -0
  49. package/skills/stitch-showcase/scripts/__pycache__/extract_zips.cpython-314.pyc +0 -0
  50. package/skills/stitch-showcase/scripts/__pycache__/parse_design_md.cpython-314.pyc +0 -0
  51. package/skills/stitch-showcase/scripts/__pycache__/slug_demangle.cpython-314.pyc +0 -0
  52. package/skills/stitch-showcase/scripts/build_showcase.py +150 -10
  53. package/skills/stitch-showcase/scripts/parse_design_md.py +145 -12
  54. package/skills/stitch-showcase/scripts/slug_demangle.py +209 -0
  55. package/skills/vscode-extension-dev/SKILL.md +90 -41
  56. package/skills/vscode-extension-dev/references/api-additional.md +168 -0
  57. package/skills/vscode-extension-dev/references/api-progress.md +55 -0
  58. package/skills/vscode-extension-dev/references/api-quickpick.md +75 -0
  59. package/skills/vscode-extension-dev/references/api-secretstorage.md +57 -0
  60. package/skills/vscode-extension-dev/references/api-statusbar.md +38 -0
  61. package/skills/vscode-extension-dev/references/api-treeview.md +78 -0
  62. package/skills/vscode-extension-dev/references/api-webview.md +149 -0
  63. package/skills/vscode-extension-dev/references/architecture.md +67 -0
  64. package/skills/vscode-extension-dev/references/debugger.md +179 -0
  65. package/skills/vscode-extension-dev/references/lsp.md +175 -0
  66. package/skills/vscode-extension-dev/references/notebooks.md +208 -0
  67. package/skills/vscode-extension-dev/references/testing.md +208 -0
  68. package/skills/vscode-extension-dev/references/api-patterns.md +0 -625
package/README.md CHANGED
@@ -40,7 +40,7 @@ claude # or gemini, or codex
40
40
 
41
41
  Installed files:
42
42
  - All skills to `~/.agents/skills/`
43
- - Platform symlinks in `~/.claude/skills/` and `~/.gemini/skills/` (Codex CLI auto-discovers from `~/.agents/skills/` — no symlink needed)
43
+ - Platform symlinks in `~/.claude/skills/` (Gemini CLI and Codex CLI auto-discover from `~/.agents/skills/` — no platform-specific symlink needed)
44
44
 
45
45
  ### Which option should I use?
46
46
 
@@ -58,7 +58,7 @@ Installed files:
58
58
  | Platform | Status | Installation Path |
59
59
  | --------------------------------------------------------- | --------- | ------------------- |
60
60
  | [Claude Code](https://claude.ai/claude-code) | Supported | `~/.claude/skills/` |
61
- | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | Supported | `~/.gemini/skills/` |
61
+ | [Gemini CLI](https://github.com/google-gemini/gemini-cli) | Supported | `~/.agents/skills/` (auto-discovered) |
62
62
  | [Codex CLI](https://developers.openai.com/codex/cli/) | Supported | `~/.agents/skills/` (auto-discovered) |
63
63
 
64
64
  All three platforms use the same Agent Skills format: a `SKILL.md` file with YAML frontmatter that tells the assistant when to use the skill and what to do.
@@ -69,10 +69,12 @@ All three platforms use the same Agent Skills format: a `SKILL.md` file with YAM
69
69
 
70
70
  | Skill | Domain | Source | Reference Files |
71
71
  | -------------------- | ------------ | ------------------------------------ | --------------- |
72
- | refactoring-ui | Design | "Refactoring UI" by Wathan & Schoger | 4 files |
73
- | humaniza | Writing (es) | Curated Spanish/es-MX style rules | 6 files |
74
- | vscode-extension-dev | VS Code | VS Code Extension API docs | 4 files |
75
- | stitch-showcase | Design Tools | Google Stitch export workflow | 14 files |
72
+ | refactoring-ui | Design | "Refactoring UI" by Wathan & Schoger | 7 files |
73
+ | humaniza | Writing (es) | Curated Spanish/es-MX style rules | 7 files |
74
+ | audit-codebase | Auditing | Evidence-based audit methodology | 2 files |
75
+ | vscode-extension-dev | VS Code | VS Code Extension API docs | 14 files |
76
+ | stitch-showcase | Design Tools | Google Stitch export workflow | 16 files |
77
+ | session-log | Project | Convention + 3 A/B rounds | 2 files |
76
78
 
77
79
  Use `aiskills list` to see available skills from the command line. Pull requests are welcome.
78
80
 
@@ -132,8 +134,6 @@ Hard restrictions:
132
134
  Distribution note:
133
135
  - Available via the plugin install (Option A above). Slash commands are not distributed by the npm CLI (Option B) because they are a Claude Code feature.
134
136
 
135
- ---
136
-
137
137
  ## How skills work
138
138
 
139
139
  Skills activate based on what you ask. You can write prompts normally:
@@ -192,7 +192,7 @@ Reference files:
192
192
 
193
193
  ### humaniza
194
194
 
195
- An editor for Spanish text (es-MX). It removes common AI writing patterns and rewrites the text so it sounds natural without changing the meaning.
195
+ An editor for Spanish text (es-MX). Based on curated es-MX style rules and adapted techniques from Hardik Pandya's Stop Slop skill (cut filler, break formulaic structures, active voice, specificity, varied rhythm). It removes common AI writing patterns and rewrites the text so it sounds natural without changing the meaning.
196
196
 
197
197
  When it activates:
198
198
  - User asks to "humanize" a text in Spanish
@@ -224,6 +224,7 @@ Reference files:
224
224
  | ----------------- | --------------------------------------------------------------- |
225
225
  | ai-patterns-es.md | AI writing tics in Spanish: inflated phrases, filler, templates |
226
226
  | lexicon-es-mx.md | Preferred es-MX vocabulary vs. Spain Spanish |
227
+ | structures-es.md | Structural patterns to avoid: binary contrasts, false agency... |
227
228
  | modes-es-mx.md | Rules per mode (marketing, technical, support, etc.) |
228
229
  | voice-es-mx.md | How to add human voice: rhythm, concreteness, variety |
229
230
  | checklist.md | Final QA before delivering the text |
@@ -238,6 +239,39 @@ Scope:
238
239
 
239
240
  ---
240
241
 
242
+ ### audit-codebase
243
+
244
+ A senior software auditor that reviews an entire project — architecture, security, compatibility, performance, maintainability, and tests — and delivers an evidence-based diagnosis with a decision matrix and correction plan. Works in two stages: a read-only audit first, then an authorized implementation of the approved plan. Every restrictive security recommendation must name the legitimate use cases it could break and the less-restrictive alternative that was evaluated. The report is written in the user's language.
245
+
246
+ When it activates:
247
+ - Asking for a full technical audit or security review of a project
248
+ - Checking if a project is ready for production
249
+ - Reviewing architecture, dependencies, compatibility, or test coverage
250
+ - Implementing an approved audit correction plan
251
+
252
+ Example prompts:
253
+ ```
254
+ "Audit this project end to end"
255
+ "Is this backend ready for production?"
256
+ "Do a security review without breaking the anonymous flows"
257
+ "Find real problems before we ship"
258
+ "Implement the audit plan we approved"
259
+ ```
260
+
261
+ Reference files:
262
+ | File | Topics |
263
+ | ---------------------- | --------------------------------------------------------------------------- |
264
+ | comprehensive-audit.md | Mandatory principles, 24-area technical scope, 5-phase method, severity |
265
+ | report-format.md | Executive summary, findings table, decision matrix, correction plan formats |
266
+
267
+ Scope:
268
+ - Stage 1 never modifies files; the first deliverable is always diagnosis + plan
269
+ - Every confirmed finding gets an explicit disposition (fix now, later, accept, won't fix)
270
+ - Findings are classified as Confirmed / Conditional risk / Unverified — no pattern-matched "vulnerabilities" without evidence
271
+ - Stage 2 only starts after the user approves the decision matrix, and implements it completely
272
+
273
+ ---
274
+
241
275
  ### vscode-extension-dev
242
276
 
243
277
  A guide for building VS Code extensions from scaffolding to publishing. Based on the official VS Code Extension API docs.
@@ -273,6 +307,52 @@ Reference files:
273
307
 
274
308
  ---
275
309
 
310
+ ### session-log
311
+
312
+ Gives a project one predictable place for its working state, so both you and any assistant know where to look instead of hunting through scattered notes. It installs a fixed four-file convention under `docs/project/` and writes a short pointer into every context file the repo has — `CLAUDE.md`, `AGENTS.md`, `GEMINI.md` — so the notes stay findable no matter which assistant opens the project next.
313
+
314
+ The convention:
315
+
316
+ | File | Holds | Loaded at startup |
317
+ | --- | --- | --- |
318
+ | `status.md` | Where the work stands: half-done things, next step, what's blocked, deployment state | **No** |
319
+ | `requirements.md` | What the system must do, and the acceptance criterion for each item | Yes |
320
+ | `decisions.md` | What was chosen and why. Append-only, dated | Yes |
321
+ | `context.md` | Documentation map, architecture, conventions, traps | Yes |
322
+
323
+ **Why `status.md` is excluded from startup.** Cached context is matched as a prefix — the first byte that differs invalidates everything after it. Status written inside a startup-loaded file means every update throws away the cache for all the stable content behind it. The file you edit most often is the one that must not load at startup.
324
+
325
+ How to use it — just say it, in whatever words you'd use anyway:
326
+
327
+ ```
328
+ "set up the project notes here — the mobile app lives at ../../Apps/MyApp"
329
+ "ya me voy, déjame anotado dónde quedé"
330
+ "where did we leave off? I haven't touched this repo in weeks"
331
+ "my CLAUDE.md has the progress and a date inside it — should I move that?"
332
+ ```
333
+
334
+ Closing a session and resuming one are different jobs and it treats them differently. On the way out it writes; on the way back in it reads `status.md` and then checks it against the repo before repeating it to you — what landed since the file was written, whether the branch it names still exists, what's uncommitted that it never mentioned. A three-week-old note is a snapshot, and the most expensive way to use one is to trust it.
335
+
336
+ There is no slash command, by design: a command and a skill doing the same job means two copies of the logic that drift apart, and a command only works in Claude Code. This is one file that Claude, Codex and Gemini all read the same way — point any of them at `skills/session-log/SKILL.md` if it doesn't pick it up on its own.
337
+
338
+ Once the convention is installed, finding the notes no longer depends on the skill at all — the pointer in `CLAUDE.md`, `AGENTS.md` and `GEMINI.md` is what any assistant reads at startup.
339
+
340
+ What it will not do:
341
+ - Commit, tag, push, or write CHANGELOG entries — that is a release, and releasing assumes the work is finished, which is the opposite of why this exists. Use `/release` for that.
342
+ - Edit your uncommitted code. It reports what it finds broken and leaves it alone.
343
+ - Invent a completion percentage. Without a fixed denominator any number is made up, so it counts what is enumerable or describes status in words.
344
+ - Write a token, a password or a client's private details into the files. They get committed, and a secret deleted in a later commit is still in the history — it records where the credential lives instead.
345
+ - Overwrite the record on arrival. If a resumed file turns out to be badly out of date it says so and offers; rewriting is your call.
346
+
347
+ Measured behaviour, across three A/B rounds against a no-skill baseline (18 runs, adversarially graded):
348
+
349
+ | | With skill | Without |
350
+ | --- | --- | --- |
351
+ | Kept volatile status out of the startup chain | 9 / 9 | 0 / 9 |
352
+ | Left the user's broken uncommitted code untouched | yes | no — fixed it unasked |
353
+
354
+ Token cost is 3–13% higher per run. **Those rounds graded an earlier layout** — a single status file versus an imported memory index — so what they establish is the split itself, not the four filenames. The paths added since (resuming against a stale file, upgrading an earlier install, monorepos, a gitignored `docs/`) have prompts written for them and have not been run. The grading notes, and an explicit account of what is and isn't measured, are in `skills/session-log/evals/`.
355
+
276
356
  ### stitch-showcase
277
357
 
278
358
  A workflow skill for processing Google Stitch design exports. It handles the full lifecycle: from raw zips to a navigable showcase, component standardization, and a visual component catalog.
package/lib/cleanup.js CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  getAgentsSkillsDir,
11
11
  getClaudeCommandsDir,
12
12
  getCodexSkillsDir,
13
+ getGeminiSkillsDir,
13
14
  } from './config.js';
14
15
  import { detectPlatforms } from './platform.js';
15
16
  import { existsSync, lstatSync, rmSync } from 'fs';
@@ -54,6 +55,20 @@ export function removeLegacySkillSymlinks(platformSkillsDir) {
54
55
  return removeSkillSymlinks(platformSkillsDir, { legacyOnly: true });
55
56
  }
56
57
 
58
+ // When the user de-selects some skills during an install, anything previously
59
+ // installed for those skills (in ~/.agents/skills/ and as platform symlinks)
60
+ // must be cleaned up so the on-disk state matches the user's selection.
61
+ export function removeUnselectedSkills(baseDir, selectedSkills) {
62
+ const skillsDir = getAgentsSkillsDir(baseDir);
63
+ const unselected = SKILLS.filter((s) => !selectedSkills.includes(s));
64
+ return removeEntriesAtDir(skillsDir, unselected, { recursive: true });
65
+ }
66
+
67
+ export function removeUnselectedSymlinks(platformSkillsDir, selectedSkills) {
68
+ const unselected = SKILLS.filter((s) => !selectedSkills.includes(s));
69
+ return removeEntriesAtDir(platformSkillsDir, unselected, { recursive: true });
70
+ }
71
+
57
72
  // Codex reads skills from the canonical ~/.agents/skills/, so any aiskills-managed
58
73
  // symlinks at ~/.codex/skills/ from earlier versions are redundant and should be
59
74
  // cleaned up. Targets both active and legacy skill names.
@@ -62,6 +77,15 @@ export function removeCodexRedundantSymlinks(baseDir) {
62
77
  return removeSkillSymlinks(codexSkillsDir);
63
78
  }
64
79
 
80
+ // Gemini CLI also auto-discovers skills from ~/.agents/skills/ per the
81
+ // agentskills.io standard. Symlinks at ~/.gemini/skills/ from earlier
82
+ // versions produce "Skill conflict detected" warnings on Gemini startup
83
+ // because the same skill is found in both locations.
84
+ export function removeGeminiRedundantSymlinks(baseDir) {
85
+ const geminiSkillsDir = getGeminiSkillsDir(baseDir);
86
+ return removeSkillSymlinks(geminiSkillsDir);
87
+ }
88
+
65
89
  export function removeSkills(baseDir, options = {}) {
66
90
  const skillsDir = getAgentsSkillsDir(baseDir);
67
91
  const skillList = getSkillList(options);
@@ -84,6 +108,7 @@ export function cleanupLegacyArtifacts(baseDir) {
84
108
  }
85
109
 
86
110
  removeCodexRedundantSymlinks(baseDir);
111
+ removeGeminiRedundantSymlinks(baseDir);
87
112
 
88
113
  if (baseDir) {
89
114
  removeSkills(undefined, { legacyOnly: true });
@@ -93,6 +118,7 @@ export function cleanupLegacyArtifacts(baseDir) {
93
118
  removeLegacySkillSymlinks(platform.skillsDir);
94
119
  }
95
120
  removeCodexRedundantSymlinks(undefined);
121
+ removeGeminiRedundantSymlinks(undefined);
96
122
  }
97
123
  }
98
124
 
@@ -102,6 +128,9 @@ export default {
102
128
  removeSkillSymlinks,
103
129
  removeLegacySkillSymlinks,
104
130
  removeCodexRedundantSymlinks,
131
+ removeGeminiRedundantSymlinks,
132
+ removeUnselectedSkills,
133
+ removeUnselectedSymlinks,
105
134
  removeSkills,
106
135
  removeCommands,
107
136
  cleanupLegacyArtifacts,
@@ -22,6 +22,9 @@ import {
22
22
  removeLegacySkillSymlinks,
23
23
  removeSkills,
24
24
  removeCommands,
25
+ removeUnselectedSkills,
26
+ removeUnselectedSymlinks,
27
+ cleanupLegacyArtifacts,
25
28
  } from '../cleanup.js';
26
29
  import {
27
30
  installSkills,
@@ -31,6 +34,7 @@ import {
31
34
  import { downloadRepoArchive } from '../downloader.js';
32
35
  import { createSkillSymlinks } from '../symlink.js';
33
36
  import { installHook } from '../hooks.js';
37
+ import { readSkillDescription, shortenSkillDescription } from '../utils.js';
34
38
  import { mkdtemp } from 'fs/promises';
35
39
  import { existsSync } from 'fs';
36
40
  import { join, resolve } from 'path';
@@ -64,6 +68,22 @@ export async function skillsCommand(options) {
64
68
  } else {
65
69
  console.log(chalk.cyan('Mode: Global installation (user home)'));
66
70
  }
71
+ const skillsLocation = isLocal
72
+ ? './.agents/skills/'
73
+ : (customPath ? `${baseDir}/.agents/skills/` : '~/.agents/skills/');
74
+ console.log('');
75
+ console.log(
76
+ chalk.yellow('▸'),
77
+ 'Skills install to',
78
+ chalk.cyan.bold(skillsLocation),
79
+ chalk.dim('(agentskills.io standard)')
80
+ );
81
+ console.log(
82
+ ' ' + chalk.dim('Read directly by Gemini, Codex, Cursor, Cline, Amp, GitHub Copilot, +more.')
83
+ );
84
+ console.log(
85
+ ' ' + chalk.dim('Below: extra symlink mirrors for Claude Code.')
86
+ );
67
87
  console.log('');
68
88
 
69
89
  // Detect installed platforms at the target base directory
@@ -85,9 +105,9 @@ export async function skillsCommand(options) {
85
105
  }
86
106
 
87
107
  if (detectedPlatforms.length === 0 && !options.path && !isLocal) {
88
- console.log(chalk.yellow('No AI coding assistants detected globally.'));
89
- console.log('Install one of: Claude Code or Gemini CLI.');
90
- console.log('(Codex CLI is supported via the canonical ~/.agents/skills/ — no platform-specific symlink needed.)');
108
+ console.log(chalk.yellow('No AI coding assistants detected globally that need platform symlinks.'));
109
+ console.log('Install Claude Code if you want aiskills to create skill symlinks for it.');
110
+ console.log('(Gemini CLI and Codex CLI auto-discover skills from ~/.agents/skills/ — no platform-specific symlink needed.)');
91
111
  console.log('Or use: aiskills install --local');
92
112
  process.exit(1);
93
113
  }
@@ -173,14 +193,73 @@ export async function skillsCommand(options) {
173
193
  }
174
194
  }
175
195
 
196
+ // Skill selection prompt — pre-checks all skills; the user can uncheck the
197
+ // ones they don't want. Skipped under --all (CI/automation) and removeOnly.
198
+ // Each line shows the skill name + a one-liner hint in dim gray, like
199
+ // skills.sh, so the rendered row height stays constant and the list does
200
+ // not "jump" when the cursor moves between skills with different-length
201
+ // descriptions.
202
+ let skillsToInstall = SKILLS;
203
+ if (!removeOnly && !options.all && !options.path) {
204
+ const skillChoices = SKILLS.map((name) => {
205
+ const fullDescription = readSkillDescription(repoDir, name);
206
+ const hint = shortenSkillDescription(fullDescription);
207
+ const label = hint
208
+ ? `${name} ${chalk.dim(`(${hint})`)}`
209
+ : name;
210
+ return { name: label, value: name, short: name, checked: true };
211
+ });
212
+
213
+ try {
214
+ const selected = await checkbox({
215
+ message: 'Select skills to install:',
216
+ choices: [...skillChoices, new Separator(' ')],
217
+ theme: {
218
+ style: {
219
+ renderSelectedChoices: () => '',
220
+ prefix: () => chalk.cyan('?'),
221
+ },
222
+ },
223
+ });
224
+
225
+ if (selected.includes('cancel')) {
226
+ console.log('Cancelled.');
227
+ process.exit(0);
228
+ }
229
+
230
+ skillsToInstall = SKILLS.filter((s) => selected.includes(s));
231
+ } catch (error) {
232
+ console.log('\nCancelled.');
233
+ process.exit(0);
234
+ }
235
+
236
+ if (skillsToInstall.length === 0) {
237
+ console.log(chalk.yellow('No skills selected. Nothing to install.'));
238
+ process.exit(0);
239
+ }
240
+ }
241
+
176
242
  const selectedPlatformNames = new Set(selectedPlatforms.map((platform) => platform.name));
177
243
 
178
244
  try {
179
245
  if (!removeOnly) {
180
246
  // Install skills
181
247
  spinner.start('Installing skills...');
182
- const skillsResult = await installSkills(repoDir, baseDir);
183
- spinner.succeed(`${SKILLS.length} skill${SKILLS.length !== 1 ? 's' : ''} installed`);
248
+ const skillsResult = await installSkills(repoDir, baseDir, skillsToInstall);
249
+ spinner.succeed(`${skillsToInstall.length} skill${skillsToInstall.length !== 1 ? 's' : ''} installed`);
250
+
251
+ // If the user deselected some skills, sweep any previously-installed
252
+ // copies and their platform symlinks so on-disk state matches selection.
253
+ if (skillsToInstall.length !== SKILLS.length) {
254
+ removeUnselectedSkills(baseDir, skillsToInstall);
255
+ }
256
+
257
+ // Sweep legacy skills, legacy commands, and redundant symlinks at platforms
258
+ // that no longer need aiskills-managed mirrors (Gemini, Codex). Without
259
+ // this, users that ran older aiskills versions keep stale symlinks at
260
+ // ~/.gemini/skills/ and ~/.codex/skills/ that Gemini reports as "Skill
261
+ // conflict detected" warnings on startup.
262
+ cleanupLegacyArtifacts(baseDir);
184
263
 
185
264
  // Install or remove slash commands based on Claude Code selection
186
265
  if (selectedPlatformNames.has('claude')) {
@@ -203,17 +282,20 @@ export async function skillsCommand(options) {
203
282
  // Create symlinks for selected platforms
204
283
  for (const platform of selectedPlatforms) {
205
284
  removeLegacySkillSymlinks(platform.skillsDir);
285
+ if (skillsToInstall.length !== SKILLS.length) {
286
+ removeUnselectedSymlinks(platform.skillsDir, skillsToInstall);
287
+ }
206
288
  spinner.start(`Linking ${platform.displayName}...`);
207
289
  const symlinkResult = await createSkillSymlinks(
208
290
  platform.skillsDir,
209
- SKILLS,
291
+ skillsToInstall,
210
292
  baseDir
211
293
  );
212
- if (symlinkResult.linked.length === SKILLS.length) {
294
+ if (symlinkResult.linked.length === skillsToInstall.length) {
213
295
  spinner.succeed(`${platform.displayName}: Skills linked`);
214
296
  } else {
215
297
  spinner.warn(
216
- `${platform.displayName}: ${symlinkResult.linked.length}/${SKILLS.length} skills linked`
298
+ `${platform.displayName}: ${symlinkResult.linked.length}/${skillsToInstall.length} skills linked`
217
299
  );
218
300
  }
219
301
  }
@@ -269,9 +351,28 @@ export async function skillsCommand(options) {
269
351
  }
270
352
  }
271
353
 
272
- // Summary
354
+ // Summary — show where the skills landed and which agents read them.
355
+ // This is the "did it actually work" confirmation the user needs.
273
356
  console.log('');
274
357
  console.log(chalk.green('✓ Skills sync complete!'));
358
+ if (!removeOnly) {
359
+ console.log('');
360
+ console.log(
361
+ ' ' + chalk.bold(skillsLocation) + chalk.dim(' (') +
362
+ chalk.dim(skillsToInstall.length + ' skill' + (skillsToInstall.length !== 1 ? 's' : '')) +
363
+ chalk.dim(')')
364
+ );
365
+ console.log(
366
+ ' ' + chalk.dim('• universal: ') +
367
+ 'Gemini, Codex, Cursor, Cline, Amp, GitHub Copilot ' + chalk.dim('+more')
368
+ );
369
+ if (selectedPlatformNames.has('claude')) {
370
+ console.log(
371
+ ' ' + chalk.dim('• symlinked: ') +
372
+ 'Claude Code ' + chalk.dim('(~/.claude/skills/)')
373
+ );
374
+ }
375
+ }
275
376
  console.log('');
276
377
 
277
378
  if (!removeOnly && !isLocal && detectOS() === 'windows') {
package/lib/config.js CHANGED
@@ -27,8 +27,10 @@ export const REPO_API_URL = 'https://api.github.com/repos/macCesar/aiskills';
27
27
 
28
28
  // Skills to install
29
29
  export const SKILLS = [
30
+ 'audit-codebase',
30
31
  'humaniza',
31
32
  'refactoring-ui',
33
+ 'session-log',
32
34
  'stitch-showcase',
33
35
  'vscode-extension-dev',
34
36
  ];
@@ -54,7 +56,23 @@ export const getClaudeCommandsDir = (baseDir = os.homedir()) => path.join(baseDi
54
56
  export const getGeminiSkillsDir = (baseDir = os.homedir()) => path.join(baseDir, '.gemini', 'skills');
55
57
  export const getCodexSkillsDir = (baseDir = os.homedir()) => path.join(baseDir, '.codex', 'skills');
56
58
 
59
+ // Name of the Claude marketplace and plugin where this CLI publishes itself.
60
+ // Used to detect when a skill is already installed via the marketplace plugin
61
+ // and avoid creating a duplicate symlink in ~/.claude/skills/.
62
+ export const CLAUDE_PLUGIN_MARKETPLACE = 'maccesar-aiskills';
63
+ export const CLAUDE_PLUGIN_NAME = 'aiskills';
64
+ export const getClaudePluginSkillsPath = (baseDir = os.homedir()) =>
65
+ path.join(baseDir, '.claude', 'plugins', 'cache', CLAUDE_PLUGIN_MARKETPLACE, CLAUDE_PLUGIN_NAME);
66
+
57
67
  // AI platform detection
68
+ //
69
+ // Only platforms that need aiskills-managed symlinks appear here.
70
+ // Gemini CLI and Codex CLI auto-discover skills from the canonical
71
+ // ~/.agents/skills/ per the agentskills.io standard, so creating
72
+ // platform-specific symlinks at ~/.gemini/skills/ or ~/.codex/skills/
73
+ // would be redundant — and in Gemini's case actively harmful, since it
74
+ // reads both locations and reports "Skill conflict detected" warnings
75
+ // when the same skill exists in both.
58
76
  export const getPlatforms = (baseDir = os.homedir()) => [
59
77
  {
60
78
  name: 'claude',
@@ -62,15 +80,6 @@ export const getPlatforms = (baseDir = os.homedir()) => [
62
80
  skillsDir: getClaudeSkillsDir(baseDir),
63
81
  configDir: path.join(baseDir, '.claude'),
64
82
  },
65
- {
66
- name: 'gemini',
67
- displayName: 'Gemini CLI',
68
- skillsDir: getGeminiSkillsDir(baseDir),
69
- configDir: path.join(baseDir, '.gemini'),
70
- },
71
- // Codex CLI is intentionally omitted: it auto-discovers skills from the
72
- // canonical ~/.agents/skills/ per the agentskills.io standard, so a
73
- // platform-specific symlink at ~/.codex/skills/ would be redundant.
74
83
  ];
75
84
 
76
85
  // API configuration
package/lib/installer.js CHANGED
@@ -61,12 +61,14 @@ export async function installSkill(repoDir, skillName, baseDir = os.homedir()) {
61
61
  }
62
62
 
63
63
  /**
64
- * Install all skills to the agents skills directory
64
+ * Install skills to the agents skills directory
65
65
  * @param {string} repoDir - Repository directory
66
66
  * @param {string} baseDir - Base directory for installation
67
+ * @param {string[]} [skillsToInstall] - Subset of skills to install (defaults to all SKILLS)
67
68
  * @returns {Promise<Object>} Results object with success/failure counts
68
69
  */
69
- export async function installSkills(repoDir, baseDir = os.homedir()) {
70
+ export async function installSkills(repoDir, baseDir = os.homedir(), skillsToInstall) {
71
+ const list = skillsToInstall || SKILLS;
70
72
  const results = {
71
73
  installed: [],
72
74
  failed: [],
@@ -83,7 +85,7 @@ export async function installSkills(repoDir, baseDir = os.homedir()) {
83
85
  results.failed.push(...legacyGlobal.failed);
84
86
  }
85
87
 
86
- for (const skill of SKILLS) {
88
+ for (const skill of list) {
87
89
  if (await installSkill(repoDir, skill, baseDir)) {
88
90
  results.installed.push(skill);
89
91
  } else {
package/lib/symlink.js CHANGED
@@ -69,19 +69,49 @@ async function removePath(path) {
69
69
  }
70
70
 
71
71
  /**
72
- * Create symlinks for all skills to a platform directory
72
+ * Check if a skill is already available via the maccesar-aiskills Claude marketplace plugin.
73
+ * When the plugin is installed, Claude Code lists the skill from
74
+ * ~/.claude/plugins/cache/maccesar-aiskills/aiskills/<version>/skills/<skill>/
75
+ * and an additional symlink at ~/.claude/skills/<skill> produces a duplicate
76
+ * entry in the slash-command autocomplete. This helper lets the symlink step
77
+ * skip Claude when the plugin already covers it.
78
+ * @param {string} skillName - Skill name (e.g. 'stitch-showcase')
79
+ * @param {string} baseDir - Optional base directory (defaults to homedir via config)
80
+ * @returns {Promise<boolean>} True if the plugin provides this skill
81
+ */
82
+ export async function isClaudePluginSkillInstalled(skillName, baseDir) {
83
+ const { readdir } = await import('fs/promises');
84
+ const { getClaudePluginSkillsPath } = await import('./config.js');
85
+ const pluginBase = getClaudePluginSkillsPath(baseDir);
86
+ if (!existsSync(pluginBase)) return false;
87
+ try {
88
+ const versions = await readdir(pluginBase);
89
+ return versions.some((v) => existsSync(join(pluginBase, v, 'skills', skillName)));
90
+ } catch {
91
+ return false;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Create symlinks for all skills to a platform directory.
97
+ * For the Claude platform, skills already provided by the maccesar-aiskills
98
+ * marketplace plugin are skipped to avoid duplicate slash-command entries;
99
+ * if a stale symlink from a previous install exists, it is removed.
73
100
  * @param {string} platformSkillsDir - Platform skills directory
74
101
  * @param {Array} skills - List of skill names
75
102
  * @param {string} baseDir - Optional base directory for target resolution
76
- * @returns {Promise<Object>} Results object with success/failure counts
103
+ * @returns {Promise<Object>} Results object with linked / failed / skipped arrays
77
104
  */
78
105
  export async function createSkillSymlinks(platformSkillsDir, skills, baseDir) {
79
- const { getAgentsSkillsDir } = await import('./config.js');
106
+ const { getAgentsSkillsDir, getClaudeSkillsDir } = await import('./config.js');
80
107
  const agentsSkillsDir = getAgentsSkillsDir(baseDir);
108
+ const claudeSkillsDir = getClaudeSkillsDir(baseDir);
109
+ const isClaudePlatform = platformSkillsDir === claudeSkillsDir;
81
110
 
82
111
  const results = {
83
112
  linked: [],
84
113
  failed: [],
114
+ skipped: [],
85
115
  };
86
116
 
87
117
  // Ensure platform directory exists
@@ -93,6 +123,17 @@ export async function createSkillSymlinks(platformSkillsDir, skills, baseDir) {
93
123
  const useRelative = !!baseDir;
94
124
 
95
125
  for (const skill of skills) {
126
+ // Skip Claude when the marketplace plugin already provides this skill;
127
+ // clean up any stale symlink left from a previous CLI install.
128
+ if (isClaudePlatform && await isClaudePluginSkillInstalled(skill, baseDir)) {
129
+ const stalePath = join(platformSkillsDir, skill);
130
+ if (existsSync(stalePath)) {
131
+ await removePath(stalePath);
132
+ }
133
+ results.skipped.push(skill);
134
+ continue;
135
+ }
136
+
96
137
  const target = join(agentsSkillsDir, skill);
97
138
  const linkPath = join(platformSkillsDir, skill);
98
139
 
@@ -148,6 +189,7 @@ export async function updateSymlink(target, path) {
148
189
  export default {
149
190
  createSymlinkOrCopy,
150
191
  createSkillSymlinks,
192
+ isClaudePluginSkillInstalled,
151
193
  isSymlink,
152
194
  resolveSymlink,
153
195
  updateSymlink,
package/lib/utils.js CHANGED
@@ -2,6 +2,45 @@
2
2
  * Utility functions
3
3
  */
4
4
 
5
+ import { readFileSync } from 'fs';
6
+ import { join } from 'path';
7
+
8
+ /**
9
+ * Read the description field from a skill's SKILL.md frontmatter.
10
+ * @param {string} repoDir - Repository directory containing skills/
11
+ * @param {string} skillName - Skill name (directory under skills/)
12
+ * @returns {string} Description text, or empty string if not available.
13
+ */
14
+ export function readSkillDescription(repoDir, skillName) {
15
+ try {
16
+ const skillPath = join(repoDir, 'skills', skillName, 'SKILL.md');
17
+ const content = readFileSync(skillPath, 'utf8');
18
+ const fmMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
19
+ if (!fmMatch) return '';
20
+ const descMatch = fmMatch[1].match(/^description:\s*["']?(.+?)["']?\s*$/m);
21
+ return descMatch ? descMatch[1].trim() : '';
22
+ } catch {
23
+ return '';
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Compress a skill description to a one-line summary suitable for an inline
29
+ * checkbox preview. Picks the first segment of the description and trims it
30
+ * to `maxLen` characters, appending an ellipsis when the source is longer.
31
+ *
32
+ * @param {string} description - The full SKILL.md description.
33
+ * @param {number} maxLen - Maximum characters of the output (excluding ellipsis).
34
+ * @returns {string} Trimmed one-liner, or empty string if input was empty.
35
+ */
36
+ export function shortenSkillDescription(description, maxLen = 60) {
37
+ if (!description) return '';
38
+ let firstSegment = description.split(/\s*[—.:;]\s*/)[0].trim();
39
+ firstSegment = firstSegment.replace(/\s+/g, ' ');
40
+ if (firstSegment.length <= maxLen) return firstSegment;
41
+ return firstSegment.slice(0, maxLen).trimEnd() + '…';
42
+ }
43
+
5
44
  /**
6
45
  * Format a list of items for display
7
46
  * @param {Array} items - Array of strings
@@ -43,6 +82,8 @@ export function compareVersions(v1, v2) {
43
82
  }
44
83
 
45
84
  export default {
85
+ readSkillDescription,
86
+ shortenSkillDescription,
46
87
  formatList,
47
88
  parseVersion,
48
89
  compareVersions,
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@maccesar/aiskills",
3
- "version": "1.12.0",
3
+ "version": "1.16.0",
4
4
  "description": "AI coding assistant skills for Claude Code, Gemini CLI, and Codex CLI",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "aiskills": "./bin/aiskills.js"
8
8
  },
9
9
  "scripts": {
10
- "test": "node --test test/**/*.test.js",
10
+ "test": "node --test test/*.test.js",
11
11
  "lint": "eslint lib/**/*.js",
12
12
  "format": "prettier --write lib/**/*.js"
13
13
  },