aicm 0.19.1 → 0.20.1

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
@@ -16,6 +16,8 @@ A CLI tool for managing Agentic configurations across projects.
16
16
  - [Features](#features)
17
17
  - [Rules](#rules)
18
18
  - [Commands](#commands)
19
+ - [Skills](#skills)
20
+ - [Agents](#agents)
19
21
  - [Hooks](#hooks)
20
22
  - [MCP Servers](#mcp-servers)
21
23
  - [Assets](#assets)
@@ -23,6 +25,7 @@ A CLI tool for managing Agentic configurations across projects.
23
25
  - [Configuration](#configuration)
24
26
  - [CLI Commands](#cli-commands)
25
27
  - [Node.js API](#nodejs-api)
28
+ - [FAQ](#faq)
26
29
 
27
30
  ## Why
28
31
 
@@ -85,6 +88,8 @@ After installation, open Cursor and ask it to do something. Your AI assistant wi
85
88
  │ ├── typescript.mdc
86
89
  │ └── react.mdc
87
90
  ├── commands/ # Command files (.md) [optional]
91
+ ├── skills/ # Agent Skills [optional]
92
+ ├── agents/ # Subagents (.md) [optional]
88
93
  ├── assets/ # Auxiliary files [optional]
89
94
  └── hooks.json # Hook configuration [optional]
90
95
  ```
@@ -139,7 +144,7 @@ The rules are now installed in `.cursor/rules/aicm/` and any MCP servers are con
139
144
  ### Notes
140
145
 
141
146
  - Generated files are always placed in subdirectories for deterministic cleanup and easy gitignore.
142
- - Users should add `.cursor/*/aicm/` to `.gitignore` to avoid tracking generated files. This single pattern covers all aicm-managed directories (rules, commands, assets, hooks).
147
+ - Users may add `.cursor/*/aicm/`, `.cursor/skills/`, `.cursor/agents/`, `.claude/`, and `.codex/` to `.gitignore` to avoid tracking generated files.
143
148
 
144
149
  ## Features
145
150
 
@@ -193,6 +198,136 @@ Configure your `aicm.json`:
193
198
 
194
199
  Command files ending in `.md` are installed to `.cursor/commands/aicm/` and appear in Cursor under the `/` command menu.
195
200
 
201
+ ### Skills
202
+
203
+ aicm supports [Agent Skills](https://agentskills.io) - a standard format for giving AI agents new capabilities and expertise. Skills are folders containing instructions, scripts, and resources that agents can discover and use.
204
+
205
+ Create a `skills/` directory where each subdirectory is a skill (containing a `SKILL.md` file):
206
+
207
+ ```
208
+ my-project/
209
+ ├── aicm.json
210
+ └── skills/
211
+ ├── pdf-processing/
212
+ │ ├── SKILL.md
213
+ │ ├── scripts/
214
+ │ │ └── extract.py
215
+ │ └── references/
216
+ │ └── REFERENCE.md
217
+ └── code-review/
218
+ └── SKILL.md
219
+ ```
220
+
221
+ Each skill must have a `SKILL.md` file with YAML frontmatter:
222
+
223
+ ```markdown
224
+ ---
225
+ name: pdf-processing
226
+ description: Extract text and tables from PDF files, fill forms, merge documents.
227
+ ---
228
+
229
+ # PDF Processing Skill
230
+
231
+ This skill enables working with PDF documents.
232
+
233
+ ## Usage
234
+
235
+ Run the extraction script:
236
+ scripts/extract.py
237
+ ```
238
+
239
+ Configure your `aicm.json`:
240
+
241
+ ```json
242
+ {
243
+ "rootDir": "./",
244
+ "targets": ["cursor"]
245
+ }
246
+ ```
247
+
248
+ Skills are installed to different locations based on the target:
249
+
250
+ | Target | Skills Location |
251
+ | ---------- | ----------------- |
252
+ | **Cursor** | `.cursor/skills/` |
253
+ | **Claude** | `.claude/skills/` |
254
+ | **Codex** | `.codex/skills/` |
255
+
256
+ When installed, each skill directory is copied in its entirety (including `scripts/`, `references/`, `assets/` subdirectories). A `.aicm.json` file is added inside each installed skill to track that it's managed by aicm.
257
+
258
+ In workspace mode, skills are installed both to each package and merged at the root level, similar to commands.
259
+
260
+ ### Agents
261
+
262
+ aicm supports [Cursor Subagents](https://cursor.com/docs/context/subagents) and [Claude Code Subagents](https://code.claude.com/docs/en/sub-agents) - specialized AI assistants that can be delegated specific tasks. Agents are markdown files with YAML frontmatter that define custom prompts, descriptions, and model configurations.
263
+
264
+ Create an `agents/` directory in your project (at the `rootDir` location):
265
+
266
+ ```
267
+ my-project/
268
+ ├── aicm.json
269
+ └── agents/
270
+ ├── code-reviewer.md
271
+ ├── debugger.md
272
+ └── specialized/
273
+ └── security-auditor.md
274
+ ```
275
+
276
+ Each agent file should have YAML frontmatter with at least a `name` and `description`:
277
+
278
+ ```markdown
279
+ ---
280
+ name: code-reviewer
281
+ description: Reviews code for quality and best practices. Use after code changes.
282
+ model: inherit
283
+ ---
284
+
285
+ You are a senior code reviewer ensuring high standards of code quality and security.
286
+
287
+ When invoked:
288
+
289
+ 1. Run git diff to see recent changes
290
+ 2. Focus on modified files
291
+ 3. Begin review immediately
292
+
293
+ Review checklist:
294
+
295
+ - Code is clear and readable
296
+ - Functions and variables are well-named
297
+ - No duplicated code
298
+ - Proper error handling
299
+ ```
300
+
301
+ Configure your `aicm.json`:
302
+
303
+ ```json
304
+ {
305
+ "rootDir": "./",
306
+ "targets": ["cursor", "claude"]
307
+ }
308
+ ```
309
+
310
+ Agents are installed to different locations based on the target:
311
+
312
+ | Target | Agents Location |
313
+ | ---------- | ----------------- |
314
+ | **Cursor** | `.cursor/agents/` |
315
+ | **Claude** | `.claude/agents/` |
316
+
317
+ A `.aicm.json` metadata file is created in the agents directory to track which agents are managed by aicm. This allows the clean command to remove only aicm-managed agents while preserving any manually created agents.
318
+
319
+ **Supported Configuration Fields:**
320
+
321
+ Only fields that work in both Cursor and Claude Code are documented:
322
+
323
+ - `name` - Unique identifier (defaults to filename without extension)
324
+ - `description` - When the agent should be used for task delegation
325
+ - `model` - Model to use (`inherit`, or platform-specific values like `sonnet`, `haiku`, `fast`)
326
+
327
+ > **Note:** Users may include additional platform-specific fields (e.g., `tools`, `hooks` for Claude Code, or `readonly`, `is_background` for Cursor) - aicm will preserve them, but they only work on the respective platform.
328
+
329
+ In workspace mode, agents are installed both to each package and merged at the root level, similar to commands and skills.
330
+
196
331
  ### Hooks
197
332
 
198
333
  aicm provides first-class support for [Cursor Agent Hooks](https://docs.cursor.com/advanced/hooks), allowing you to intercept and extend the agent's behavior. Hooks enable you to run custom scripts before/after shell execution, file edits, MCP calls, and more.
@@ -369,9 +504,11 @@ aicm automatically detects workspaces if your `package.json` contains a `workspa
369
504
  ### How It Works
370
505
 
371
506
  1. **Discover packages**: Automatically find all directories containing `aicm.json` files in your repository.
372
- 2. **Install per package**: Install rules and MCPs for each package individually in their respective directories.
507
+ 2. **Install per package**: Install rules, commands, skills, and agents for each package individually in their respective directories.
373
508
  3. **Merge MCP servers**: Write a merged `.cursor/mcp.json` at the repository root containing all MCP servers from every package.
374
509
  4. **Merge commands**: Write a merged `.cursor/commands/aicm/` at the repository root containing all commands from every package.
510
+ 5. **Merge skills**: Write merged skills to the repository root (e.g., `.cursor/skills/`) containing all skills from every package.
511
+ 6. **Merge agents**: Write merged agents to the repository root (e.g., `.cursor/agents/`) containing all agents from every package.
375
512
 
376
513
  For example, in a workspace structure like:
377
514
 
@@ -429,7 +566,7 @@ Create an `aicm.json` file in your project root, or an `aicm` key in your projec
429
566
 
430
567
  ### Configuration Options
431
568
 
432
- - **rootDir**: Directory containing your aicm structure. Must contain one or more of: `rules/`, `commands/`, `assets/`, `hooks/`, or `hooks.json`. If not specified, aicm will only install rules from presets and will not pick up any local directories.
569
+ - **rootDir**: Directory containing your aicm structure. Must contain one or more of: `rules/`, `commands/`, `skills/`, `agents/`, `assets/`, `hooks/`, or `hooks.json`. If not specified, aicm will only install rules from presets and will not pick up any local directories.
433
570
  - **targets**: IDEs/Agent targets where rules should be installed. Defaults to `["cursor"]`. Supported targets: `cursor`, `windsurf`, `codex`, `claude`.
434
571
  - **presets**: List of preset packages or paths to include.
435
572
  - **mcpServers**: MCP server configurations.
@@ -471,11 +608,16 @@ aicm uses a convention-based directory structure:
471
608
  ```
472
609
  my-project/
473
610
  ├── aicm.json
474
- ├── rules/ # Rule files (.mdc) [required for rules]
611
+ ├── rules/ # Rule files (.mdc) [optional]
475
612
  │ ├── api.mdc
476
613
  │ └── testing.mdc
477
614
  ├── commands/ # Command files (.md) [optional]
478
615
  │ └── generate.md
616
+ ├── skills/ # Agent Skills [optional]
617
+ │ └── my-skill/
618
+ │ └── SKILL.md
619
+ ├── agents/ # Subagents (.md) [optional]
620
+ │ └── code-reviewer.md
479
621
  ├── assets/ # Auxiliary files [optional]
480
622
  │ ├── schema.json
481
623
  │ └── examples/
@@ -559,6 +701,50 @@ install({
559
701
 
560
702
  To prevent [prompt-injection](https://en.wikipedia.org/wiki/Prompt_injection), use only packages from trusted sources.
561
703
 
704
+ ## FAQ
705
+
706
+ ### Can I reference rules from commands or vice versa?
707
+
708
+ **No, direct references between rules and commands are not supported.** This is because:
709
+
710
+ - **Commands are hoisted** to the root level in workspace mode (`.cursor/commands/aicm/`)
711
+ - **Rules remain nested** at the package level (`package-a/.cursor/rules/aicm/`)
712
+ - This creates broken relative paths when commands try to reference rules
713
+
714
+ **❌ Don't do this:**
715
+
716
+ ```markdown
717
+ <!-- commands/validate.md -->
718
+
719
+ Follow the rules in [api-rule.mdc](../rules/api-rule.mdc) <!-- BROKEN! -->
720
+ ```
721
+
722
+ **✅ Do this instead:**
723
+
724
+ ```markdown
725
+ <!-- Put shared content in assets/coding-standards.md -->
726
+
727
+ # Coding Standards
728
+
729
+ - Use TypeScript for all new code
730
+ - Follow ESLint rules
731
+ - Write unit tests for all functions
732
+ ```
733
+
734
+ ```markdown
735
+ <!-- rules/api-rule.mdc -->
736
+
737
+ Follow the coding standards in [coding-standards.md](../assets/coding-standards.md).
738
+ ```
739
+
740
+ ```markdown
741
+ <!-- commands/validate.md -->
742
+
743
+ Validate against our [coding standards](../assets/coding-standards.md).
744
+ ```
745
+
746
+ Use shared assets for content that needs to be referenced by both rules and commands. Assets are properly rewritten and work in all modes.
747
+
562
748
  ## Contributing
563
749
 
564
750
  Contributions are welcome! Please feel free to open an issue or submit a Pull Request.
package/dist/bin/aicm.js CHANGED
File without changes
@@ -152,6 +152,110 @@ function cleanHooks(cwd, verbose) {
152
152
  }
153
153
  return hasChanges;
154
154
  }
155
+ /**
156
+ * Clean aicm-managed skills from a skills directory
157
+ * Only removes skills that have .aicm.json (presence indicates aicm management)
158
+ */
159
+ function cleanSkills(cwd, verbose) {
160
+ let cleanedCount = 0;
161
+ // Skills directories for each target
162
+ const skillsDirs = [
163
+ node_path_1.default.join(cwd, ".cursor", "skills"),
164
+ node_path_1.default.join(cwd, ".claude", "skills"),
165
+ node_path_1.default.join(cwd, ".codex", "skills"),
166
+ ];
167
+ for (const skillsDir of skillsDirs) {
168
+ if (!fs_extra_1.default.existsSync(skillsDir)) {
169
+ continue;
170
+ }
171
+ try {
172
+ const entries = fs_extra_1.default.readdirSync(skillsDir, { withFileTypes: true });
173
+ for (const entry of entries) {
174
+ if (!entry.isDirectory()) {
175
+ continue;
176
+ }
177
+ const skillPath = node_path_1.default.join(skillsDir, entry.name);
178
+ const metadataPath = node_path_1.default.join(skillPath, ".aicm.json");
179
+ // Only clean skills that have .aicm.json (presence indicates aicm management)
180
+ if (fs_extra_1.default.existsSync(metadataPath)) {
181
+ fs_extra_1.default.removeSync(skillPath);
182
+ if (verbose) {
183
+ console.log(chalk_1.default.gray(` Removed skill ${skillPath}`));
184
+ }
185
+ cleanedCount++;
186
+ }
187
+ }
188
+ // Remove the skills directory if it's now empty
189
+ const remainingEntries = fs_extra_1.default.readdirSync(skillsDir);
190
+ if (remainingEntries.length === 0) {
191
+ fs_extra_1.default.removeSync(skillsDir);
192
+ if (verbose) {
193
+ console.log(chalk_1.default.gray(` Removed empty directory ${skillsDir}`));
194
+ }
195
+ }
196
+ }
197
+ catch (_a) {
198
+ console.warn(chalk_1.default.yellow(`Warning: Failed to clean skills in ${skillsDir}`));
199
+ }
200
+ }
201
+ return cleanedCount;
202
+ }
203
+ /**
204
+ * Clean aicm-managed agents from agents directories
205
+ * Only removes agents that are tracked in .aicm.json metadata file
206
+ */
207
+ function cleanAgents(cwd, verbose) {
208
+ let cleanedCount = 0;
209
+ // Agents directories for each target
210
+ const agentsDirs = [
211
+ node_path_1.default.join(cwd, ".cursor", "agents"),
212
+ node_path_1.default.join(cwd, ".claude", "agents"),
213
+ ];
214
+ for (const agentsDir of agentsDirs) {
215
+ const metadataPath = node_path_1.default.join(agentsDir, ".aicm.json");
216
+ if (!fs_extra_1.default.existsSync(metadataPath)) {
217
+ continue;
218
+ }
219
+ try {
220
+ const metadata = fs_extra_1.default.readJsonSync(metadataPath);
221
+ // Remove all managed agents (names only)
222
+ for (const agentName of metadata.managedAgents || []) {
223
+ // Skip invalid names containing path separators (security check)
224
+ if (agentName.includes("/") || agentName.includes("\\")) {
225
+ console.warn(chalk_1.default.yellow(`Warning: Skipping invalid agent name "${agentName}" (contains path separator)`));
226
+ continue;
227
+ }
228
+ const fullPath = node_path_1.default.join(agentsDir, agentName + ".md");
229
+ if (fs_extra_1.default.existsSync(fullPath)) {
230
+ fs_extra_1.default.removeSync(fullPath);
231
+ if (verbose) {
232
+ console.log(chalk_1.default.gray(` Removed agent ${fullPath}`));
233
+ }
234
+ cleanedCount++;
235
+ }
236
+ }
237
+ // Remove the metadata file
238
+ fs_extra_1.default.removeSync(metadataPath);
239
+ if (verbose) {
240
+ console.log(chalk_1.default.gray(` Removed ${metadataPath}`));
241
+ }
242
+ // Remove the agents directory if it's now empty
243
+ if (fs_extra_1.default.existsSync(agentsDir)) {
244
+ const remainingEntries = fs_extra_1.default.readdirSync(agentsDir);
245
+ if (remainingEntries.length === 0) {
246
+ fs_extra_1.default.removeSync(agentsDir);
247
+ if (verbose) {
248
+ console.log(chalk_1.default.gray(` Removed empty directory ${agentsDir}`));
249
+ }
250
+ }
251
+ }
252
+ }
253
+ catch (_a) {
254
+ console.warn(chalk_1.default.yellow(`Warning: Failed to clean agents in ${agentsDir}`));
255
+ }
256
+ }
257
+ return cleanedCount;
258
+ }
155
259
  function cleanEmptyDirectories(cwd, verbose) {
156
260
  let cleanedCount = 0;
157
261
  const dirsToCheck = [
@@ -159,7 +263,14 @@ function cleanEmptyDirectories(cwd, verbose) {
159
263
  node_path_1.default.join(cwd, ".cursor", "commands"),
160
264
  node_path_1.default.join(cwd, ".cursor", "assets"),
161
265
  node_path_1.default.join(cwd, ".cursor", "hooks"),
266
+ node_path_1.default.join(cwd, ".cursor", "skills"),
267
+ node_path_1.default.join(cwd, ".cursor", "agents"),
162
268
  node_path_1.default.join(cwd, ".cursor"),
269
+ node_path_1.default.join(cwd, ".claude", "skills"),
270
+ node_path_1.default.join(cwd, ".claude", "agents"),
271
+ node_path_1.default.join(cwd, ".claude"),
272
+ node_path_1.default.join(cwd, ".codex", "skills"),
273
+ node_path_1.default.join(cwd, ".codex"),
163
274
  ];
164
275
  for (const dir of dirsToCheck) {
165
276
  if (fs_extra_1.default.existsSync(dir)) {
@@ -211,6 +322,10 @@ async function cleanPackage(options = {}) {
211
322
  // Clean hooks
212
323
  if (cleanHooks(cwd, verbose))
213
324
  cleanedCount++;
325
+ // Clean skills
326
+ cleanedCount += cleanSkills(cwd, verbose);
327
+ // Clean agents
328
+ cleanedCount += cleanAgents(cwd, verbose);
214
329
  // Clean empty directories
215
330
  cleanedCount += cleanEmptyDirectories(cwd, verbose);
216
331
  return {
@@ -41,6 +41,97 @@ function collectWorkspaceCommandTargets(packages) {
41
41
  }
42
42
  return Array.from(targets);
43
43
  }
44
+ /**
45
+ * Merge skills from multiple workspace packages
46
+ * Skills are merged flat (not namespaced by preset)
47
+ * Dedupes preset skills that appear in multiple packages
48
+ */
49
+ function mergeWorkspaceSkills(packages) {
50
+ var _a;
51
+ const skills = [];
52
+ const seenPresetSkills = new Set();
53
+ for (const pkg of packages) {
54
+ // Skills are supported by cursor, claude, and codex targets
55
+ const hasSkillsTarget = pkg.config.config.targets.includes("cursor") ||
56
+ pkg.config.config.targets.includes("claude") ||
57
+ pkg.config.config.targets.includes("codex");
58
+ if (!hasSkillsTarget) {
59
+ continue;
60
+ }
61
+ for (const skill of (_a = pkg.config.skills) !== null && _a !== void 0 ? _a : []) {
62
+ if (skill.presetName) {
63
+ // Dedupe preset skills by preset+name combination
64
+ const presetKey = `${skill.presetName}::${skill.name}`;
65
+ if (seenPresetSkills.has(presetKey)) {
66
+ continue;
67
+ }
68
+ seenPresetSkills.add(presetKey);
69
+ }
70
+ skills.push(skill);
71
+ }
72
+ }
73
+ return skills;
74
+ }
75
+ /**
76
+ * Collect all targets that support skills from workspace packages
77
+ */
78
+ function collectWorkspaceSkillTargets(packages) {
79
+ const targets = new Set();
80
+ for (const pkg of packages) {
81
+ for (const target of pkg.config.config.targets) {
82
+ // Skills are supported by cursor, claude, and codex
83
+ if (target === "cursor" || target === "claude" || target === "codex") {
84
+ targets.add(target);
85
+ }
86
+ }
87
+ }
88
+ return Array.from(targets);
89
+ }
90
+ /**
91
+ * Merge agents from multiple workspace packages
92
+ * Agents are merged flat (not namespaced by preset)
93
+ * Dedupes preset agents that appear in multiple packages
94
+ */
95
+ function mergeWorkspaceAgents(packages) {
96
+ var _a;
97
+ const agents = [];
98
+ const seenPresetAgents = new Set();
99
+ for (const pkg of packages) {
100
+ // Agents are supported by cursor and claude targets
101
+ const hasAgentsTarget = pkg.config.config.targets.includes("cursor") ||
102
+ pkg.config.config.targets.includes("claude");
103
+ if (!hasAgentsTarget) {
104
+ continue;
105
+ }
106
+ for (const agent of (_a = pkg.config.agents) !== null && _a !== void 0 ? _a : []) {
107
+ if (agent.presetName) {
108
+ // Dedupe preset agents by preset+name combination
109
+ const presetKey = `${agent.presetName}::${agent.name}`;
110
+ if (seenPresetAgents.has(presetKey)) {
111
+ continue;
112
+ }
113
+ seenPresetAgents.add(presetKey);
114
+ }
115
+ agents.push(agent);
116
+ }
117
+ }
118
+ return agents;
119
+ }
120
+ /**
121
+ * Collect all targets that support agents from workspace packages
122
+ */
123
+ function collectWorkspaceAgentTargets(packages) {
124
+ const targets = new Set();
125
+ for (const pkg of packages) {
126
+ for (const target of pkg.config.config.targets) {
127
+ // Agents are supported by cursor and claude
128
+ if (target === "cursor" || target === "claude") {
129
+ targets.add(target);
130
+ }
131
+ }
132
+ }
133
+ return Array.from(targets);
134
+ }
44
135
  function mergeWorkspaceMcpServers(packages) {
45
136
  const merged = {};
46
137
  const info = {};
@@ -101,6 +192,8 @@ async function installWorkspacesPackages(packages, options = {}) {
101
192
  let totalCommandCount = 0;
102
193
  let totalAssetCount = 0;
103
194
  let totalHookCount = 0;
195
+ let totalSkillCount = 0;
196
+ let totalAgentCount = 0;
104
197
  // Install packages sequentially for now (can be parallelized later)
105
198
  for (const pkg of packages) {
106
199
  const packagePath = pkg.absolutePath;
@@ -114,6 +207,8 @@ async function installWorkspacesPackages(packages, options = {}) {
114
207
  totalCommandCount += result.installedCommandCount;
115
208
  totalAssetCount += result.installedAssetCount;
116
209
  totalHookCount += result.installedHookCount;
210
+ totalSkillCount += result.installedSkillCount;
211
+ totalAgentCount += result.installedAgentCount;
117
212
  results.push({
118
213
  path: pkg.relativePath,
119
214
  success: result.success,
@@ -122,6 +217,8 @@ async function installWorkspacesPackages(packages, options = {}) {
122
217
  installedCommandCount: result.installedCommandCount,
123
218
  installedAssetCount: result.installedAssetCount,
124
219
  installedHookCount: result.installedHookCount,
220
+ installedSkillCount: result.installedSkillCount,
221
+ installedAgentCount: result.installedAgentCount,
125
222
  });
126
223
  }
127
224
  catch (error) {
@@ -133,6 +230,8 @@ async function installWorkspacesPackages(packages, options = {}) {
133
230
  installedCommandCount: 0,
134
231
  installedAssetCount: 0,
135
232
  installedHookCount: 0,
233
+ installedSkillCount: 0,
234
+ installedAgentCount: 0,
136
235
  });
137
236
  }
138
237
  }
@@ -144,6 +243,8 @@ async function installWorkspacesPackages(packages, options = {}) {
144
243
  totalCommandCount,
145
244
  totalAssetCount,
146
245
  totalHookCount,
246
+ totalSkillCount,
247
+ totalAgentCount,
147
248
  };
148
249
  }
149
250
  /**
@@ -162,11 +263,13 @@ async function installWorkspaces(cwd, installOnCI, verbose = false, dryRun = fal
162
263
  const isRoot = pkg.relativePath === ".";
163
264
  if (!isRoot)
164
265
  return true;
165
- // For root directories, only keep if it has rules, commands, or presets
266
+ // For root directories, only keep if it has rules, commands, skills, agents, or presets
166
267
  const hasRules = pkg.config.rules && pkg.config.rules.length > 0;
167
268
  const hasCommands = pkg.config.commands && pkg.config.commands.length > 0;
269
+ const hasSkills = pkg.config.skills && pkg.config.skills.length > 0;
270
+ const hasAgents = pkg.config.agents && pkg.config.agents.length > 0;
168
271
  const hasPresets = pkg.config.config.presets && pkg.config.config.presets.length > 0;
169
- return hasRules || hasCommands || hasPresets;
272
+ return hasRules || hasCommands || hasSkills || hasAgents || hasPresets;
170
273
  });
171
274
  if (packages.length === 0) {
172
275
  return {
@@ -176,6 +279,8 @@ async function installWorkspaces(cwd, installOnCI, verbose = false, dryRun = fal
176
279
  installedCommandCount: 0,
177
280
  installedAssetCount: 0,
178
281
  installedHookCount: 0,
282
+ installedSkillCount: 0,
283
+ installedAgentCount: 0,
179
284
  packagesCount: 0,
180
285
  };
181
286
  }
@@ -206,6 +311,30 @@ async function installWorkspaces(cwd, installOnCI, verbose = false, dryRun = fal
206
311
  (0, install_1.writeAssetsToTargets)(allAssets, workspaceCommandTargets);
207
312
  (0, install_1.writeCommandsToTargets)(dedupedWorkspaceCommands, workspaceCommandTargets);
208
313
  }
314
+ // Merge and write skills for workspace
315
+ const workspaceSkills = mergeWorkspaceSkills(packages);
316
+ const workspaceSkillTargets = collectWorkspaceSkillTargets(packages);
317
+ if (workspaceSkills.length > 0) {
318
+ (0, install_1.warnPresetSkillCollisions)(workspaceSkills);
319
+ }
320
+ if (!dryRun &&
321
+ workspaceSkills.length > 0 &&
322
+ workspaceSkillTargets.length > 0) {
323
+ const dedupedWorkspaceSkills = (0, install_1.dedupeSkillsForInstall)(workspaceSkills);
324
+ (0, install_1.writeSkillsToTargets)(dedupedWorkspaceSkills, workspaceSkillTargets);
325
+ }
326
+ // Merge and write agents for workspace
327
+ const workspaceAgents = mergeWorkspaceAgents(packages);
328
+ const workspaceAgentTargets = collectWorkspaceAgentTargets(packages);
329
+ if (workspaceAgents.length > 0) {
330
+ (0, install_1.warnPresetAgentCollisions)(workspaceAgents);
331
+ }
332
+ if (!dryRun &&
333
+ workspaceAgents.length > 0 &&
334
+ workspaceAgentTargets.length > 0) {
335
+ const dedupedWorkspaceAgents = (0, install_1.dedupeAgentsForInstall)(workspaceAgents);
336
+ (0, install_1.writeAgentsToTargets)(dedupedWorkspaceAgents, workspaceAgentTargets);
337
+ }
209
338
  const { merged: rootMcp, conflicts } = mergeWorkspaceMcpServers(packages);
210
339
  const hasCursorTarget = packages.some((p) => p.config.config.targets.includes("cursor"));
211
340
  if (!dryRun && hasCursorTarget && Object.keys(rootMcp).length > 0) {
@@ -231,6 +360,12 @@ async function installWorkspaces(cwd, installOnCI, verbose = false, dryRun = fal
231
360
  if (pkg.installedHookCount > 0) {
232
361
  summaryParts.push(`${pkg.installedHookCount} hook${pkg.installedHookCount === 1 ? "" : "s"}`);
233
362
  }
363
+ if (pkg.installedSkillCount > 0) {
364
+ summaryParts.push(`${pkg.installedSkillCount} skill${pkg.installedSkillCount === 1 ? "" : "s"}`);
365
+ }
366
+ if (pkg.installedAgentCount > 0) {
367
+ summaryParts.push(`${pkg.installedAgentCount} agent${pkg.installedAgentCount === 1 ? "" : "s"}`);
368
+ }
234
369
  console.log(chalk_1.default.green(`✅ ${pkg.path} (${summaryParts.join(", ")})`));
235
370
  }
236
371
  else {
@@ -248,7 +383,13 @@ async function installWorkspaces(cwd, installOnCI, verbose = false, dryRun = fal
248
383
  const hookSummary = result.totalHookCount > 0
249
384
  ? `, ${result.totalHookCount} hook${result.totalHookCount === 1 ? "" : "s"} total`
250
385
  : "";
251
- console.log(chalk_1.default.green(`Successfully installed: ${result.packages.length - failedPackages.length}/${result.packages.length} packages (${result.totalRuleCount} rule${result.totalRuleCount === 1 ? "" : "s"} total${commandSummary}${hookSummary})`));
386
+ const skillSummary = result.totalSkillCount > 0
387
+ ? `, ${result.totalSkillCount} skill${result.totalSkillCount === 1 ? "" : "s"} total`
388
+ : "";
389
+ const agentSummary = result.totalAgentCount > 0
390
+ ? `, ${result.totalAgentCount} agent${result.totalAgentCount === 1 ? "" : "s"} total`
391
+ : "";
392
+ console.log(chalk_1.default.green(`Successfully installed: ${result.packages.length - failedPackages.length}/${result.packages.length} packages (${result.totalRuleCount} rule${result.totalRuleCount === 1 ? "" : "s"} total${commandSummary}${hookSummary}${skillSummary}${agentSummary})`));
252
393
  console.log(chalk_1.default.red(`Failed packages: ${failedPackages.map((p) => p.path).join(", ")}`));
253
394
  }
254
395
  const errorDetails = failedPackages
@@ -261,6 +402,8 @@ async function installWorkspaces(cwd, installOnCI, verbose = false, dryRun = fal
261
402
  installedCommandCount: result.totalCommandCount,
262
403
  installedAssetCount: result.totalAssetCount,
263
404
  installedHookCount: result.totalHookCount,
405
+ installedSkillCount: result.totalSkillCount,
406
+ installedAgentCount: result.totalAgentCount,
264
407
  packagesCount: result.packages.length,
265
408
  };
266
409
  }
@@ -270,6 +413,8 @@ async function installWorkspaces(cwd, installOnCI, verbose = false, dryRun = fal
270
413
  installedCommandCount: result.totalCommandCount,
271
414
  installedAssetCount: result.totalAssetCount,
272
415
  installedHookCount: result.totalHookCount,
416
+ installedSkillCount: result.totalSkillCount,
417
+ installedAgentCount: result.totalAgentCount,
273
418
  packagesCount: result.packages.length,
274
419
  };
275
420
  });