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 +190 -4
- package/dist/bin/aicm.js +0 -0
- package/dist/commands/clean.js +115 -0
- package/dist/commands/install-workspaces.js +148 -3
- package/dist/commands/install.d.ts +35 -1
- package/dist/commands/install.js +253 -3
- package/dist/utils/config.d.ts +21 -0
- package/dist/utils/config.js +106 -4
- package/dist/utils/safe-path.d.ts +10 -0
- package/dist/utils/safe-path.js +28 -0
- package/package.json +18 -16
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
|
|
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
|
|
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) [
|
|
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
|
package/dist/commands/clean.js
CHANGED
|
@@ -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
|
-
|
|
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
|
});
|