a11y-devkit-deploy 0.9.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,13 +18,15 @@ npx a11y-devkit-deploy
18
18
  a11y-devkit-deploy
19
19
  ```
20
20
 
21
- ### Flags
22
-
23
- - `--local` / `--global`: Skip the scope prompt.
24
- - `--yes`: Use defaults (local scope, all IDEs, install skills).
25
- - `--uninstall`: Remove skills and MCP entries installed by this tool.
26
-
27
- ## After Installation
21
+ ### Flags
22
+
23
+ - `--local` / `--global`: Skip the scope prompt.
24
+ - `--yes`: Use defaults (local scope, all IDEs, install skills).
25
+ - `--uninstall`: Remove skills and MCP entries installed by this tool.
26
+
27
+ **Note:** MCP configs are always written globally (AppData/Application Support). The scope flag only affects skills.
28
+
29
+ ## After Installation
28
30
 
29
31
  Once installation completes, you'll find a comprehensive usage guide in your IDE's skills directory:
30
32
 
@@ -86,16 +88,16 @@ This CLI automates the setup of accessibility tooling by:
86
88
  - MCP servers to configure
87
89
  - Even the IDE list itself
88
90
 
89
- **Adding Support for a New IDE** takes just 5 lines of JSON:
90
- ```json
91
- {
92
- "id": "new-ide",
93
- "displayName": "New IDE",
94
- "mcpServerKey": "servers",
95
- "skillsFolder": ".new-ide/skills",
96
- "mcpConfigFile": ".new-ide/mcp.json"
97
- }
98
- ```
91
+ **Adding Support for a New IDE** takes just 5 lines of JSON:
92
+ ```json
93
+ {
94
+ "id": "new-ide",
95
+ "displayName": "New IDE",
96
+ "mcpServerKey": "servers",
97
+ "skillsFolder": ".new-ide/skills",
98
+ "mcpConfigFile": ".new-ide/mcp.json"
99
+ }
100
+ ```
99
101
 
100
102
  **Safe by Default** - Won't overwrite your existing:
101
103
  - Custom MCP servers in your IDE configs
@@ -193,38 +195,34 @@ Add an object to the `hostApplications` array with the host application's config
193
195
 
194
196
  ```json
195
197
  {
196
- "hostApplications": [
197
- {
198
- "id": "windsurf",
199
- "displayName": "Windsurf",
200
- "mcpServerKey": "servers",
201
- "skillsFolder": ".codeium/windsurf/skills",
202
- "mcpConfigFile": ".codeium/windsurf/mcp_config.json"
203
- },
204
- {
205
- "id": "vscode",
206
- "displayName": "VSCode",
207
- "mcpServerKey": "servers",
208
- "skillsFolder": ".github/skills",
209
- "mcpConfigFile": ".github/mcp.json",
210
- "globalMcpConfigFile": "Code/User/mcp.json"
211
- }
212
- ]
213
- }
214
- ```
215
-
216
- **Note:** The `globalMcpConfigFile` property is optional. When specified, the global MCP config path is relative to the platform's app support directory (AppData on Windows, Application Support on macOS) instead of the home directory.
217
-
218
- **Host Application Configuration Properties:**
219
- - `id` - Unique identifier for the host application
220
- - `displayName` - Human-readable name shown in prompts
221
- - `mcpServerKey` - MCP config key name (`"servers"`, `"mcpServers"`, or `"mcp_servers"` for TOML)
222
- - `skillsFolder` - Path to skills directory (relative to home/project root)
223
- - `mcpConfigFile` - Path to MCP config file (relative to home/project root). Supports both JSON (`.json`) and TOML (`.toml`) formats. TOML format is auto-detected by file extension (used by Codex).
224
- - `globalMcpConfigFile` - (Optional) Path to global MCP config relative to AppData/Application Support instead of home directory. Used for hosts like VSCode that store configs in platform-specific app directories:
225
- - Windows: `%APPDATA%` (e.g., `C:\Users\name\AppData\Roaming`)
226
- - macOS: `~/Library/Application Support`
227
- - Linux: `$XDG_CONFIG_HOME` or `~/.config`
198
+ "hostApplications": [
199
+ {
200
+ "id": "windsurf",
201
+ "displayName": "Windsurf",
202
+ "mcpServerKey": "servers",
203
+ "skillsFolder": ".codeium/windsurf/skills",
204
+ "mcpConfigFile": ".codeium/windsurf/mcp_config.json"
205
+ },
206
+ {
207
+ "id": "vscode",
208
+ "displayName": "VSCode",
209
+ "mcpServerKey": "servers",
210
+ "skillsFolder": ".github/skills",
211
+ "mcpConfigFile": "Code/User/mcp.json"
212
+ }
213
+ ]
214
+ }
215
+ ```
216
+
217
+ **Host Application Configuration Properties:**
218
+ - `id` - Unique identifier for the host application
219
+ - `displayName` - Human-readable name shown in prompts
220
+ - `mcpServerKey` - MCP config key name (`"servers"`, `"mcpServers"`, or `"mcp_servers"` for TOML)
221
+ - `skillsFolder` - Path to skills directory (relative to home/project root)
222
+ - `mcpConfigFile` - Path to MCP config file (relative to AppData/Application Support). Supports both JSON (`.json`) and TOML (`.toml`) formats. TOML format is auto-detected by file extension (used by Codex):
223
+ - Windows: `%APPDATA%` (e.g., `C:\Users\name\AppData\Roaming`)
224
+ - macOS: `~/Library/Application Support`
225
+ - Linux: `$XDG_CONFIG_HOME` or `~/.config`
228
226
 
229
227
  **Note:** Codex uses TOML format for its MCP configuration (`~/.codex/config.toml`), which requires the `mcpServerKey` to be `"mcp_servers"` and generates config entries like:
230
228
  ```toml
@@ -252,53 +250,48 @@ The CLI **safely merges** with existing configurations:
252
250
 
253
251
  ## Directory Structure
254
252
 
255
- ### Local Install (Project-Specific)
256
- ```
257
- your-project/
258
- ├── .claude/
259
- ├── mcp.json # Claude Code MCP config
260
- │ └── skills/ # Claude Code skills
261
- ├── .cursor/
262
- ├── mcp.json # Cursor MCP config
263
- │ └── skills/ # Cursor skills
264
- ├── .codex/
265
- ├── config.toml # Codex MCP config
266
- │ └── skills/ # Codex skills
267
- ├── .github/
268
- │ ├── mcp.json # VSCode MCP config
269
- └── skills/ # VSCode skills
270
- ├── .codeium/windsurf/
271
- │ ├── mcp_config.json # Windsurf MCP config
272
- │ └── skills/ # Windsurf skills
273
- └── .factory/
274
- ├── mcp.json # Factory MCP config
275
- └── skills/ # Factory skills
276
- ```
277
-
278
- ### Global Install (User-Wide)
279
- ```
280
- ~/.claude/
281
- ├── mcp.json # Claude Code global MCP config
282
- └── skills/ # Claude Code global skills
283
- ~/.cursor/
284
- ├── mcp.json # Cursor global MCP config
285
- └── skills/ # Cursor global skills
286
- ~/.codex/
287
- ├── config.toml # Codex global MCP config
288
- └── skills/ # Codex global skills
289
- ~/.github/
290
- └── skills/ # VSCode global skills
291
- ~/.codeium/windsurf/
292
- ├── mcp_config.json # Windsurf global MCP config
293
- └── skills/ # Windsurf global skills
294
- ~/.factory/
295
- ├── mcp.json # Factory global MCP config
296
- └── skills/ # Factory global skills
297
-
298
- # VSCode MCP config lives in AppData/Application Support:
299
- # Windows: %APPDATA%/Code/User/mcp.json
300
- # macOS: ~/Library/Application Support/Code/User/mcp.json
301
- ```
253
+ ### Local Install (Project-Specific)
254
+ ```
255
+ your-project/
256
+ ├── .claude/
257
+ └── skills/ # Claude Code skills
258
+ ├── .cursor/
259
+ │ └── skills/ # Cursor skills
260
+ ├── .codex/
261
+ │ └── skills/ # Codex skills
262
+ ├── .github/
263
+ └── skills/ # VSCode skills
264
+ ├── .codeium/windsurf/
265
+ │ └── skills/ # Windsurf skills
266
+ └── .factory/
267
+ └── skills/ # Factory skills
268
+ ```
269
+
270
+ ### Global Install (User-Wide)
271
+ ```
272
+ ~/.claude/
273
+ └── skills/ # Claude Code global skills
274
+ ~/.cursor/
275
+ └── skills/ # Cursor global skills
276
+ ~/.codex/
277
+ └── skills/ # Codex global skills
278
+ ~/.github/
279
+ └── skills/ # VSCode global skills
280
+ ~/.codeium/windsurf/
281
+ └── skills/ # Windsurf global skills
282
+ ~/.factory/
283
+ └── skills/ # Factory global skills
284
+
285
+ # MCP config files live in AppData/Application Support:
286
+ # Windows: %APPDATA%/.claude.json
287
+ # Windows: %APPDATA%/.cursor/mcp.json
288
+ # Windows: %APPDATA%/.codex/config.toml
289
+ # Windows: %APPDATA%/Code/User/mcp.json
290
+ # Windows: %APPDATA%/.codeium/windsurf/mcp_config.json
291
+ # Windows: %APPDATA%/.factory/mcp.json
292
+ # macOS: ~/Library/Application Support/<same paths as above>
293
+ # Linux: $XDG_CONFIG_HOME/<same paths as above> (or ~/.config)
294
+ ```
302
295
 
303
296
  **Note:** Paths are fully customizable per IDE in `config/settings.json`
304
297
 
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "skillsFolder": "a11y",
3
3
  "readmeTemplate": "deploy-README.md",
4
- "supportLocalMcpInstallation": false,
5
4
  "skills": [
6
5
  {
7
6
  "name": "A11y Base Web Skill",
@@ -50,51 +49,50 @@
50
49
  {
51
50
  "id": "claude",
52
51
  "displayName": "Claude Code",
53
- "mcpServerKey": "servers",
54
- "globalMcpServerKey": "mcpServers",
52
+ "mcpServerKey": "mcpServers",
55
53
  "skillsFolder": ".claude/skills",
56
- "mcpConfigFile": ".claude.json"
54
+ "mcpConfigFile": ".claude.json",
55
+ "nestSkillsFolder": false
57
56
  },
58
57
  {
59
58
  "id": "cursor",
60
59
  "displayName": "Cursor",
61
60
  "mcpServerKey": "mcpServers",
62
- "globalMcpServerKey": "mcpServers",
63
61
  "skillsFolder": ".cursor/skills",
64
- "mcpConfigFile": ".cursor/mcp.json"
62
+ "mcpConfigFile": ".cursor/mcp.json",
63
+ "nestSkillsFolder": true
65
64
  },
66
65
  {
67
66
  "id": "codex",
68
67
  "displayName": "Codex",
69
68
  "mcpServerKey": "mcp_servers",
70
- "globalMcpServerKey": "mcp_servers",
71
69
  "skillsFolder": ".codex/skills",
72
- "mcpConfigFile": ".codex/config.toml"
70
+ "mcpConfigFile": ".codex/config.toml",
71
+ "nestSkillsFolder": true
73
72
  },
74
73
  {
75
74
  "id": "vscode",
76
75
  "displayName": "VSCode",
77
76
  "mcpServerKey": "servers",
78
- "globalMcpServerKey": "servers",
79
77
  "skillsFolder": ".github/skills",
80
- "mcpConfigFile": ".github/mcp.json",
81
- "globalMcpConfigFile": "Code/User/mcp.json"
78
+ "mcpConfigFile": "Code/User/mcp.json",
79
+ "nestSkillsFolder": true
82
80
  },
83
81
  {
84
82
  "id": "windsurf",
85
83
  "displayName": "Windsurf",
86
84
  "mcpServerKey": "servers",
87
- "globalMcpServerKey": "servers",
88
85
  "skillsFolder": ".codeium/windsurf/skills",
89
- "mcpConfigFile": ".codeium/windsurf/mcp_config.json"
86
+ "mcpConfigFile": ".codeium/windsurf/mcp_config.json",
87
+ "nestSkillsFolder": true
90
88
  },
91
89
  {
92
90
  "id": "factory",
93
91
  "displayName": "Factory",
94
92
  "mcpServerKey": "mcpServers",
95
- "globalMcpServerKey": "mcpServers",
96
93
  "skillsFolder": ".factory/skills",
97
- "mcpConfigFile": ".factory/mcp.json"
94
+ "mcpConfigFile": ".factory/mcp.json",
95
+ "nestSkillsFolder": true
98
96
  }
99
97
  ],
100
98
  "mcpServers": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "a11y-devkit-deploy",
3
- "version": "0.9.8",
3
+ "version": "1.0.0",
4
4
  "description": "CLI to deploy a11y skills and MCP servers across IDEs",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/cli.js CHANGED
@@ -231,7 +231,6 @@ async function run() {
231
231
  }));
232
232
 
233
233
  let scope = args.scope;
234
- let mcpScope = null;
235
234
  let hostSelection = config.hostApplications.map((host) => host.id);
236
235
 
237
236
  if (!args.autoYes) {
@@ -250,26 +249,6 @@ async function run() {
250
249
  ],
251
250
  initial: 0,
252
251
  },
253
- {
254
- type: config.supportLocalMcpInstallation ? "select" : null,
255
- name: "mcpScope",
256
- message: "Install MCP configs locally or globally?",
257
- choices: [
258
- {
259
- title: `Local to this project (${formatPath(projectRoot)})`,
260
- value: "local",
261
- description:
262
- "Write to project-level host application config folders (version-controllable)",
263
- },
264
- {
265
- title: "Global for this user",
266
- value: "global",
267
- description:
268
- "Write to user-level host application config folders",
269
- },
270
- ],
271
- initial: 0,
272
- },
273
252
  {
274
253
  type: "multiselect",
275
254
  name: "hosts",
@@ -287,18 +266,12 @@ async function run() {
287
266
  );
288
267
 
289
268
  scope = scope || response.scope;
290
- mcpScope =
291
- response.mcpScope ||
292
- (config.supportLocalMcpInstallation ? "local" : "global");
293
269
  hostSelection = response.hosts || hostSelection;
294
270
  }
295
271
 
296
272
  if (!scope) {
297
273
  scope = "local";
298
274
  }
299
- if (!mcpScope) {
300
- mcpScope = config.supportLocalMcpInstallation ? "local" : "global";
301
- }
302
275
 
303
276
  if (!hostSelection.length) {
304
277
  warn(
@@ -308,7 +281,7 @@ async function run() {
308
281
  }
309
282
 
310
283
  info(`Skills scope: ${scope === "local" ? "Local" : "Global"}`);
311
- info(`MCP scope: ${mcpScope === "local" ? "Local" : "Global"}`);
284
+ info("MCP configs: Global (user-level)");
312
285
 
313
286
  // Create temp directory for npm install
314
287
  const tempDir = path.join(getTempDir(), `.a11y-devkit-${Date.now()}`);
@@ -316,10 +289,17 @@ async function run() {
316
289
  const skillsSpinner = startSpinner("Installing skills from npm...");
317
290
 
318
291
  try {
319
- const skillTargets =
320
- scope === "local"
321
- ? hostSelection.map((host) => hostPaths[host].localSkillsDir)
322
- : hostSelection.map((host) => hostPaths[host].skillsDir);
292
+ const skillTargets = hostSelection.map((hostId) => {
293
+ const host = config.hostApplications.find((h) => h.id === hostId);
294
+ const targetPath =
295
+ scope === "local"
296
+ ? hostPaths[hostId].localSkillsDir
297
+ : hostPaths[hostId].skillsDir;
298
+ return {
299
+ path: targetPath,
300
+ shouldNest: host.nestSkillsFolder ?? true, // Default to true for backwards compatibility
301
+ };
302
+ });
323
303
 
324
304
  const skillNames = skillsToInstall.map((skill) =>
325
305
  typeof skill === "string" ? skill : skill.npmName,
@@ -340,16 +320,11 @@ async function run() {
340
320
 
341
321
  // Configure MCP servers using npx (no local installation needed!)
342
322
  const mcpSpinner = startSpinner("Updating MCP configurations...");
343
- const mcpConfigPaths =
344
- mcpScope === "local"
345
- ? hostSelection.map((host) => hostPaths[host].localMcpConfig)
346
- : hostSelection.map((host) => hostPaths[host].mcpConfig);
323
+ const mcpConfigPaths = hostSelection.map((host) => hostPaths[host].mcpConfig);
347
324
 
348
325
  for (let i = 0; i < hostSelection.length; i++) {
349
326
  const host = hostSelection[i];
350
- const serverKey = mcpScope === "global"
351
- ? hostPaths[host].globalMcpServerKey
352
- : hostPaths[host].mcpServerKey;
327
+ const serverKey = hostPaths[host].mcpServerKey;
353
328
  await installMcpConfig(
354
329
  mcpConfigPaths[i],
355
330
  mcpServersToInstall,
@@ -358,7 +333,7 @@ async function run() {
358
333
  );
359
334
  }
360
335
  mcpSpinner.succeed(
361
- `MCP configs updated for ${hostSelection.length} host application(s) (${mcpScope} scope).`,
336
+ `MCP configs updated for ${hostSelection.length} host application(s) (global scope).`,
362
337
  );
363
338
 
364
339
  // Clean up temporary directory
@@ -371,11 +346,15 @@ async function run() {
371
346
  info("MCP servers use npx - no local installation needed!");
372
347
  console.log("");
373
348
  success("Next Steps:");
374
- const skillsFolderPath = config.skillsFolder ? `${config.skillsFolder}/` : "";
375
- const skillsPath =
376
- scope === "local"
377
- ? `.claude/skills/${skillsFolderPath}README.md (or your host application's equivalent)`
378
- : `~/.claude/skills/${skillsFolderPath}README.md (or your host application's global skills directory)`;
349
+ // Determine the README path based on the first selected host's nesting preference
350
+ const firstHost = config.hostApplications.find((h) => h.id === hostSelection[0]);
351
+ const firstHostSkillsPath = scope === "local"
352
+ ? hostPaths[hostSelection[0]].localSkillsDir
353
+ : hostPaths[hostSelection[0]].skillsDir;
354
+ const skillsFolderPath = (firstHost?.nestSkillsFolder ?? true) && config.skillsFolder
355
+ ? `${config.skillsFolder}/`
356
+ : "";
357
+ const skillsPath = `${firstHostSkillsPath}/${skillsFolderPath}a11y-devkit-README.md`;
379
358
  info(`📖 Check ${skillsPath} for comprehensive usage guide`);
380
359
  info("✨ Includes 70+ example prompts for all skills and MCP servers");
381
360
  info(
@@ -400,7 +379,6 @@ async function runUninstall(
400
379
  let removeSkills = true;
401
380
  let removeMcp = true;
402
381
  let scope = args.scope;
403
- let mcpScope = null;
404
382
  let hostSelection = config.hostApplications.map((host) => host.id);
405
383
 
406
384
  if (!args.autoYes) {
@@ -457,29 +435,6 @@ async function runUninstall(
457
435
  });
458
436
  }
459
437
 
460
- if (removeMcp && config.supportLocalMcpInstallation) {
461
- questions.push({
462
- type: "select",
463
- name: "mcpScope",
464
- message: "Remove MCP configs locally or globally?",
465
- choices: [
466
- {
467
- title: `Local to this project (${formatPath(projectRoot)})`,
468
- value: "local",
469
- description:
470
- "Remove from project-level host application config folders (version-controllable)",
471
- },
472
- {
473
- title: "Global for this user",
474
- value: "global",
475
- description:
476
- "Remove from user-level host application config folders",
477
- },
478
- ],
479
- initial: 0,
480
- });
481
- }
482
-
483
438
  questions.push({
484
439
  type: "multiselect",
485
440
  name: "hosts",
@@ -496,7 +451,6 @@ async function runUninstall(
496
451
  });
497
452
 
498
453
  scope = scope || response.scope;
499
- mcpScope = response.mcpScope || mcpScope;
500
454
  hostSelection = response.hosts || hostSelection;
501
455
  }
502
456
 
@@ -504,10 +458,6 @@ async function runUninstall(
504
458
  scope = "local";
505
459
  }
506
460
 
507
- if (removeMcp && !mcpScope) {
508
- mcpScope = config.supportLocalMcpInstallation ? "local" : "global";
509
- }
510
-
511
461
  if (!hostSelection.length) {
512
462
  warn(
513
463
  "No host applications selected. Uninstall requires at least one host application.",
@@ -519,16 +469,23 @@ async function runUninstall(
519
469
  info(`Skills scope: ${scope === "local" ? "Local" : "Global"}`);
520
470
  }
521
471
  if (removeMcp) {
522
- info(`MCP scope: ${mcpScope === "local" ? "Local" : "Global"}`);
472
+ info("MCP configs: Global (user-level)");
523
473
  }
524
474
 
525
475
  if (removeSkills) {
526
476
  const skillsSpinner = startSpinner("Removing skills...");
527
477
  try {
528
- const skillTargets =
529
- scope === "local"
530
- ? hostSelection.map((host) => hostPaths[host].localSkillsDir)
531
- : hostSelection.map((host) => hostPaths[host].skillsDir);
478
+ const skillTargets = hostSelection.map((hostId) => {
479
+ const host = config.hostApplications.find((h) => h.id === hostId);
480
+ const targetPath =
481
+ scope === "local"
482
+ ? hostPaths[hostId].localSkillsDir
483
+ : hostPaths[hostId].skillsDir;
484
+ return {
485
+ path: targetPath,
486
+ shouldNest: host.nestSkillsFolder ?? true, // Default to true for backwards compatibility
487
+ };
488
+ });
532
489
 
533
490
  const skillNames = config.skills.map((skill) =>
534
491
  typeof skill === "string" ? skill : skill.npmName,
@@ -550,19 +507,14 @@ async function runUninstall(
550
507
 
551
508
  if (removeMcp) {
552
509
  const mcpSpinner = startSpinner("Removing MCP configurations...");
553
- const mcpConfigPaths =
554
- mcpScope === "local"
555
- ? hostSelection.map((host) => hostPaths[host].localMcpConfig)
556
- : hostSelection.map((host) => hostPaths[host].mcpConfig);
510
+ const mcpConfigPaths = hostSelection.map((host) => hostPaths[host].mcpConfig);
557
511
 
558
512
  let removedCount = 0;
559
513
  const serverNames = config.mcpServers.map((server) => server.name);
560
514
 
561
515
  for (let i = 0; i < hostSelection.length; i++) {
562
516
  const host = hostSelection[i];
563
- const serverKey = mcpScope === "global"
564
- ? hostPaths[host].globalMcpServerKey
565
- : hostPaths[host].mcpServerKey;
517
+ const serverKey = hostPaths[host].mcpServerKey;
566
518
  const result = await removeMcpConfig(
567
519
  mcpConfigPaths[i],
568
520
  serverNames,
@@ -573,7 +525,7 @@ async function runUninstall(
573
525
 
574
526
  if (removedCount > 0) {
575
527
  mcpSpinner.succeed(
576
- `Removed ${removedCount} MCP entries from ${hostSelection.length} host application(s) (${mcpScope} scope).`,
528
+ `Removed ${removedCount} MCP entries from ${hostSelection.length} host application(s) (global scope).`,
577
529
  );
578
530
  } else {
579
531
  mcpSpinner.succeed("No matching MCP entries found to remove.");
@@ -641,40 +593,6 @@ async function runGitMcpInstallation(
641
593
  },
642
594
  );
643
595
 
644
- // Prompt for MCP Config Scope (where to write MCP configurations)
645
- // Skip if local MCP installation is not supported
646
- let mcpScopeResponse = {
647
- mcpScope: config.supportLocalMcpInstallation ? null : "global",
648
- };
649
-
650
- if (config.supportLocalMcpInstallation) {
651
- mcpScopeResponse = await prompts(
652
- {
653
- type: "select",
654
- name: "mcpScope",
655
- message: "Where to write MCP configurations?",
656
- choices: [
657
- {
658
- title: `Local to this project (${formatPath(projectRoot)})`,
659
- value: "local",
660
- description: "Write to project-level IDE config folders",
661
- },
662
- {
663
- title: "Global for this user",
664
- value: "global",
665
- description: "Write to user-level IDE config folders",
666
- },
667
- ],
668
- initial: 0,
669
- },
670
- {
671
- onCancel: () => {
672
- warn("Git MCP installation cancelled.");
673
- process.exit(0);
674
- },
675
- },
676
- );
677
- }
678
596
 
679
597
  // Prompt for host application selection
680
598
  const hostChoices = config.hostApplications.map((host) => ({
@@ -708,10 +626,9 @@ async function runGitMcpInstallation(
708
626
  }
709
627
 
710
628
  const repoScope = repoScopeResponse.repoScope;
711
- const mcpScope = mcpScopeResponse.mcpScope;
712
629
 
713
630
  info(`Repository clone scope: ${repoScope === "local" ? "Local" : "Global"}`);
714
- info(`MCP config scope: ${mcpScope === "local" ? "Local" : "Global"}`);
631
+ info("MCP configs: Global (user-level)");
715
632
 
716
633
  // Install Git MCP
717
634
  const gitSpinner = startSpinner("Cloning Git repository...");
@@ -741,10 +658,7 @@ async function runGitMcpInstallation(
741
658
  // Install MCP configurations to selected host applications
742
659
  const mcpConfigSpinner = startSpinner("Updating MCP configurations...");
743
660
 
744
- const mcpConfigPaths =
745
- mcpScope === "local"
746
- ? hostSelection.map((host) => hostPaths[host].localMcpConfig)
747
- : hostSelection.map((host) => hostPaths[host].mcpConfig);
661
+ const mcpConfigPaths = hostSelection.map((host) => hostPaths[host].mcpConfig);
748
662
 
749
663
  // Construct the MCP server configuration with absolute path
750
664
  const mcpServerConfig = {
@@ -770,9 +684,7 @@ async function runGitMcpInstallation(
770
684
 
771
685
  for (let i = 0; i < hostSelection.length; i++) {
772
686
  const host = hostSelection[i];
773
- const serverKey = mcpScope === "global"
774
- ? hostPaths[host].globalMcpServerKey
775
- : hostPaths[host].mcpServerKey;
687
+ const serverKey = hostPaths[host].mcpServerKey;
776
688
  await installMcpConfig(
777
689
  mcpConfigPaths[i],
778
690
  [mcpServerConfig],
@@ -782,7 +694,7 @@ async function runGitMcpInstallation(
782
694
  }
783
695
 
784
696
  mcpConfigSpinner.succeed(
785
- `MCP configs updated for ${hostSelection.length} host application(s) (${mcpScope} scope).`,
697
+ `MCP configs updated for ${hostSelection.length} host application(s) (global scope).`,
786
698
  );
787
699
 
788
700
  // Display success message
@@ -77,7 +77,7 @@ async function removeDirIfEmpty(targetDir) {
77
77
  * 4. Returns temp directory path for cleanup
78
78
  *
79
79
  * @param {string[]} skills - Array of npm package names
80
- * @param {string[]} targetDirs - Array of target directories to install skills to
80
+ * @param {Array<{path: string, shouldNest: boolean}>} targetConfigs - Array of target configs with path and nesting preference
81
81
  * @param {string} tempDir - Temporary directory for npm install
82
82
  * @param {string} skillsFolder - Optional subfolder name to bundle skills (e.g., "a11y")
83
83
  * @param {string} readmeTemplate - README template filename from templates folder
@@ -85,7 +85,7 @@ async function removeDirIfEmpty(targetDir) {
85
85
  */
86
86
  async function installSkillsFromNpm(
87
87
  skills,
88
- targetDirs,
88
+ targetConfigs,
89
89
  tempDir,
90
90
  skillsFolder = null,
91
91
  readmeTemplate = "deploy-README.md",
@@ -117,9 +117,12 @@ async function installSkillsFromNpm(
117
117
  const nodeModulesDir = path.join(tempDir, "node_modules");
118
118
  let installedCount = 0;
119
119
 
120
- for (const targetDir of targetDirs) {
120
+ for (const targetConfig of targetConfigs) {
121
+ const targetDir = targetConfig.path;
122
+ const shouldNest = targetConfig.shouldNest;
123
+
121
124
  // Determine the actual skills directory (with or without bundle folder)
122
- const skillsDir = skillsFolder
125
+ const skillsDir = shouldNest && skillsFolder
123
126
  ? path.join(targetDir, skillsFolder)
124
127
  : targetDir;
125
128
 
@@ -156,7 +159,7 @@ async function installSkillsFromNpm(
156
159
  }
157
160
 
158
161
  return {
159
- installed: installedCount / targetDirs.length,
162
+ installed: installedCount / targetConfigs.length,
160
163
  tempDir,
161
164
  };
162
165
  }
@@ -165,21 +168,24 @@ async function installSkillsFromNpm(
165
168
  * Remove skills installed by this tool from target directories.
166
169
  *
167
170
  * @param {string[]} skills - Array of npm package names
168
- * @param {string[]} targetDirs - Array of target directories to uninstall from
171
+ * @param {Array<{path: string, shouldNest: boolean}>} targetConfigs - Array of target configs with path and nesting preference
169
172
  * @param {string} skillsFolder - Optional subfolder name used for bundled skills
170
173
  * @param {string} readmeTemplate - README template filename from templates folder
171
174
  * @returns {Promise<{removed: number}>}
172
175
  */
173
176
  async function uninstallSkillsFromTargets(
174
177
  skills,
175
- targetDirs,
178
+ targetConfigs,
176
179
  skillsFolder = null,
177
180
  readmeTemplate = "deploy-README.md",
178
181
  ) {
179
182
  let removedCount = 0;
180
183
 
181
- for (const targetDir of targetDirs) {
182
- const skillsDir = skillsFolder
184
+ for (const targetConfig of targetConfigs) {
185
+ const targetDir = targetConfig.path;
186
+ const shouldNest = targetConfig.shouldNest;
187
+
188
+ const skillsDir = shouldNest && skillsFolder
183
189
  ? path.join(targetDir, skillsFolder)
184
190
  : targetDir;
185
191
 
@@ -198,7 +204,7 @@ async function uninstallSkillsFromTargets(
198
204
  await fs.rm(readmePath, { force: true });
199
205
  }
200
206
 
201
- if (skillsFolder) {
207
+ if (shouldNest && skillsFolder) {
202
208
  await removeDirIfEmpty(skillsDir);
203
209
  }
204
210
  }
package/src/paths.js CHANGED
@@ -36,32 +36,28 @@ function getHostApplicationPaths(projectRoot, platformInfo = getPlatform(), host
36
36
  const paths = {};
37
37
 
38
38
  for (const host of hostConfigs) {
39
- // Default paths for local scope (relative to home or project)
39
+ // Default paths for global scope (relative to home)
40
40
  const skillsFolder = host.skillsFolder || `.${host.id}/skills`;
41
41
  const mcpConfigFile = host.mcpConfigFile || `.${host.id}/mcp.json`;
42
42
 
43
- // MCP config: use AppData/Application Support if globalMcpConfigFile specified, otherwise home
43
+ // MCP config: use AppData/Application Support for config files
44
44
  // appSupport resolves to:
45
45
  // - Windows: %APPDATA% (e.g., C:\Users\name\AppData\Roaming)
46
46
  // - macOS: ~/Library/Application Support
47
47
  // - Linux: $XDG_CONFIG_HOME or ~/.config
48
- const globalMcpConfig = host.globalMcpConfigFile
49
- ? path.join(appSupport, host.globalMcpConfigFile)
50
- : path.join(home, mcpConfigFile);
48
+ const mcpConfig = path.join(appSupport, mcpConfigFile);
51
49
 
52
- // Skills always use home directory (skills live at project level, not in AppData)
53
- const globalSkillsDir = path.join(home, skillsFolder);
50
+ // Skills always use home directory
51
+ const skillsDir = path.join(home, skillsFolder);
54
52
 
55
- paths[host.id] = {
56
- name: host.displayName,
57
- mcpConfig: globalMcpConfig,
58
- localMcpConfig: path.join(projectRoot, mcpConfigFile),
59
- mcpServerKey: host.mcpServerKey,
60
- globalMcpServerKey: host.globalMcpServerKey || host.mcpServerKey,
61
- skillsDir: globalSkillsDir,
62
- localSkillsDir: path.join(projectRoot, skillsFolder)
63
- };
64
- }
53
+ paths[host.id] = {
54
+ name: host.displayName,
55
+ mcpConfig: mcpConfig,
56
+ mcpServerKey: host.mcpServerKey,
57
+ skillsDir: skillsDir,
58
+ localSkillsDir: path.join(projectRoot, skillsFolder) // Still needed for skills scope
59
+ };
60
+ }
65
61
 
66
62
  return paths;
67
63
  }
@@ -81,4 +77,4 @@ export {
81
77
  getHostApplicationPaths,
82
78
  getTempDir,
83
79
  getMcpRepoDir
84
- };
80
+ };