a11y-devkit-deploy 0.9.1 → 0.9.3
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 +9 -7
- package/package.json +1 -1
- package/src/cli.js +203 -4
- package/src/installers/mcp.js +51 -1
- package/src/installers/skills.js +62 -2
- /package/config/{a11y.json → settings.json} +0 -0
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ a11y-devkit-deploy
|
|
|
22
22
|
|
|
23
23
|
- `--local` / `--global`: Skip the scope prompt.
|
|
24
24
|
- `--yes`: Use defaults (local scope, all IDEs, install skills).
|
|
25
|
+
- `--uninstall`: Remove skills and MCP entries installed by this tool.
|
|
25
26
|
|
|
26
27
|
## After Installation
|
|
27
28
|
|
|
@@ -63,7 +64,7 @@ All MCP servers are configured to run via `npx`, which means:
|
|
|
63
64
|
|
|
64
65
|
This CLI automates the setup of accessibility tooling by:
|
|
65
66
|
|
|
66
|
-
1. **Installing skills from npm** - Downloads and installs accessibility skill packages (configurable in `config/
|
|
67
|
+
1. **Installing skills from npm** - Downloads and installs accessibility skill packages (configurable in `config/settings.json`)
|
|
67
68
|
2. **Configuring MCP servers** - Updates each IDE's MCP config to enable accessibility-focused MCP servers (also configurable)
|
|
68
69
|
|
|
69
70
|
**Default configuration includes:**
|
|
@@ -79,7 +80,7 @@ This CLI automates the setup of accessibility tooling by:
|
|
|
79
80
|
|
|
80
81
|
## Why This Tool?
|
|
81
82
|
|
|
82
|
-
**Zero Hardcoded Values** - Every aspect of the tool is driven by `config/
|
|
83
|
+
**Zero Hardcoded Values** - Every aspect of the tool is driven by `config/settings.json`:
|
|
83
84
|
- IDE paths and configuration files
|
|
84
85
|
- Skills to install
|
|
85
86
|
- MCP servers to configure
|
|
@@ -103,7 +104,7 @@ This CLI automates the setup of accessibility tooling by:
|
|
|
103
104
|
|
|
104
105
|
### Skills Installed (Default)
|
|
105
106
|
|
|
106
|
-
The following skill packages are installed from npm by default. **Add your own by editing `config/
|
|
107
|
+
The following skill packages are installed from npm by default. **Add your own by editing `config/settings.json`**:
|
|
107
108
|
|
|
108
109
|
| Skill | Package | Description |
|
|
109
110
|
|-------|---------|-------------|
|
|
@@ -142,7 +143,7 @@ The generated MCP config looks like this:
|
|
|
142
143
|
|
|
143
144
|
## Configuration
|
|
144
145
|
|
|
145
|
-
The entire tool is **fully config-driven**. Edit `config/
|
|
146
|
+
The entire tool is **fully config-driven**. Edit `config/settings.json` to customize everything without touching code.
|
|
146
147
|
|
|
147
148
|
### Adding a New Skill
|
|
148
149
|
|
|
@@ -292,11 +293,11 @@ your-project/
|
|
|
292
293
|
# macOS: ~/Library/Application Support/Code/User/mcp.json
|
|
293
294
|
```
|
|
294
295
|
|
|
295
|
-
**Note:** Paths are fully customizable per IDE in `config/
|
|
296
|
+
**Note:** Paths are fully customizable per IDE in `config/settings.json`
|
|
296
297
|
|
|
297
298
|
## MCP Servers Included (Default)
|
|
298
299
|
|
|
299
|
-
**Add your own by editing `config/
|
|
300
|
+
**Add your own by editing `config/settings.json`**:
|
|
300
301
|
|
|
301
302
|
| Server | Package | Description |
|
|
302
303
|
|--------|---------|-------------|
|
|
@@ -308,7 +309,7 @@ your-project/
|
|
|
308
309
|
|
|
309
310
|
## Complete Config Example
|
|
310
311
|
|
|
311
|
-
Here's what a complete `config/
|
|
312
|
+
Here's what a complete `config/settings.json` looks like:
|
|
312
313
|
|
|
313
314
|
```json
|
|
314
315
|
{
|
|
@@ -365,3 +366,4 @@ Here's what a complete `config/a11y.json` looks like:
|
|
|
365
366
|
```
|
|
366
367
|
|
|
367
368
|
Everything is customizable - add, remove, or modify any section to match your needs.
|
|
369
|
+
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -25,8 +25,8 @@ import prompts from "prompts";
|
|
|
25
25
|
|
|
26
26
|
import { header, info, warn, success, startSpinner, formatPath } from "./ui.js";
|
|
27
27
|
import { getPlatform, getHostApplicationPaths, getTempDir, getMcpRepoDir } from "./paths.js";
|
|
28
|
-
import { installSkillsFromNpm, cleanupTemp } from "./installers/skills.js";
|
|
29
|
-
import { installMcpConfig } from "./installers/mcp.js";
|
|
28
|
+
import { installSkillsFromNpm, uninstallSkillsFromTargets, cleanupTemp } from "./installers/skills.js";
|
|
29
|
+
import { installMcpConfig, removeMcpConfig } from "./installers/mcp.js";
|
|
30
30
|
import { getGitMcpPrompts, parseArgsString } from "./prompts/git-mcp.js";
|
|
31
31
|
import { installGitMcp } from "./installers/git-mcp.js";
|
|
32
32
|
|
|
@@ -34,7 +34,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
34
34
|
const __dirname = path.dirname(__filename);
|
|
35
35
|
|
|
36
36
|
async function loadConfig() {
|
|
37
|
-
const configPath = path.join(__dirname, "..", "config", "
|
|
37
|
+
const configPath = path.join(__dirname, "..", "config", "settings.json");
|
|
38
38
|
const raw = await fs.readFile(configPath, "utf8");
|
|
39
39
|
return JSON.parse(raw);
|
|
40
40
|
}
|
|
@@ -55,6 +55,7 @@ function parseArgs(argv) {
|
|
|
55
55
|
? "local"
|
|
56
56
|
: null,
|
|
57
57
|
gitMcp: args.has("--git-mcp"),
|
|
58
|
+
uninstall: args.has("--uninstall"),
|
|
58
59
|
};
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -80,10 +81,22 @@ async function run() {
|
|
|
80
81
|
`A11y Devkit Deploy v${pkg.version}`,
|
|
81
82
|
args.gitMcp
|
|
82
83
|
? "Install MCP server from Git repository"
|
|
83
|
-
:
|
|
84
|
+
: args.uninstall
|
|
85
|
+
? "Uninstall skills + MCP servers installed by this tool"
|
|
86
|
+
: "Install skills + MCP servers across host applications",
|
|
84
87
|
);
|
|
85
88
|
info(`Detected OS: ${formatOs(platformInfo)}`);
|
|
86
89
|
|
|
90
|
+
if (args.uninstall && args.gitMcp) {
|
|
91
|
+
warn("--uninstall cannot be used with --git-mcp.");
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (args.uninstall) {
|
|
96
|
+
await runUninstall(projectRoot, platformInfo, config, hostPaths, args);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
87
100
|
// Branch to Git MCP installation flow
|
|
88
101
|
if (args.gitMcp) {
|
|
89
102
|
await runGitMcpInstallation(projectRoot, platformInfo, config, hostPaths, args);
|
|
@@ -340,6 +353,191 @@ async function run() {
|
|
|
340
353
|
info("Documentation: https://github.com/joe-watkins/a11y-devkit#readme");
|
|
341
354
|
}
|
|
342
355
|
|
|
356
|
+
async function runUninstall(projectRoot, platformInfo, config, hostPaths, args) {
|
|
357
|
+
console.log("\n");
|
|
358
|
+
info("Removing skills and MCP servers installed by this tool");
|
|
359
|
+
console.log("");
|
|
360
|
+
|
|
361
|
+
let removeSkills = true;
|
|
362
|
+
let removeMcp = true;
|
|
363
|
+
let scope = args.scope;
|
|
364
|
+
let mcpScope = null;
|
|
365
|
+
let hostSelection = config.hostApplications.map((host) => host.id);
|
|
366
|
+
|
|
367
|
+
if (!args.autoYes) {
|
|
368
|
+
const removeResponse = await prompts(
|
|
369
|
+
{
|
|
370
|
+
type: "multiselect",
|
|
371
|
+
name: "targets",
|
|
372
|
+
message: "Remove which items?",
|
|
373
|
+
choices: [
|
|
374
|
+
{ title: "Skills", value: "skills" },
|
|
375
|
+
{ title: "MCP servers", value: "mcp" },
|
|
376
|
+
],
|
|
377
|
+
initial: [0, 1],
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
onCancel: () => {
|
|
381
|
+
warn("Uninstall cancelled.");
|
|
382
|
+
process.exit(0);
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
const selectedTargets = removeResponse.targets || [];
|
|
388
|
+
removeSkills = selectedTargets.includes("skills");
|
|
389
|
+
removeMcp = selectedTargets.includes("mcp");
|
|
390
|
+
|
|
391
|
+
if (!removeSkills && !removeMcp) {
|
|
392
|
+
warn("No items selected to uninstall.");
|
|
393
|
+
process.exit(0);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (!args.autoYes) {
|
|
398
|
+
const hostChoices = config.hostApplications.map((host) => ({
|
|
399
|
+
title: host.displayName,
|
|
400
|
+
value: host.id,
|
|
401
|
+
}));
|
|
402
|
+
|
|
403
|
+
const questions = [];
|
|
404
|
+
|
|
405
|
+
if (removeSkills && !scope) {
|
|
406
|
+
questions.push({
|
|
407
|
+
type: "select",
|
|
408
|
+
name: "scope",
|
|
409
|
+
message: "Remove skills locally or globally?",
|
|
410
|
+
choices: [
|
|
411
|
+
{
|
|
412
|
+
title: `Local to this project (${formatPath(projectRoot)}) [recommended]`,
|
|
413
|
+
value: "local",
|
|
414
|
+
},
|
|
415
|
+
{ title: "Global for this user", value: "global" },
|
|
416
|
+
],
|
|
417
|
+
initial: 0,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (removeMcp) {
|
|
422
|
+
questions.push({
|
|
423
|
+
type: "select",
|
|
424
|
+
name: "mcpScope",
|
|
425
|
+
message: "Remove MCP configs locally or globally?",
|
|
426
|
+
choices: [
|
|
427
|
+
{
|
|
428
|
+
title: `Local to this project (${formatPath(projectRoot)})`,
|
|
429
|
+
value: "local",
|
|
430
|
+
description:
|
|
431
|
+
"Remove from project-level host application config folders (version-controllable)",
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
title: "Global for this user",
|
|
435
|
+
value: "global",
|
|
436
|
+
description: "Remove from user-level host application config folders",
|
|
437
|
+
},
|
|
438
|
+
],
|
|
439
|
+
initial: 0,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
questions.push({
|
|
444
|
+
type: "multiselect",
|
|
445
|
+
name: "hosts",
|
|
446
|
+
message: "Remove from which host applications?",
|
|
447
|
+
choices: hostChoices,
|
|
448
|
+
initial: hostChoices.map((_, index) => index),
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
const response = await prompts(questions, {
|
|
452
|
+
onCancel: () => {
|
|
453
|
+
warn("Uninstall cancelled.");
|
|
454
|
+
process.exit(0);
|
|
455
|
+
},
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
scope = scope || response.scope;
|
|
459
|
+
mcpScope = response.mcpScope || mcpScope;
|
|
460
|
+
hostSelection = response.hosts || hostSelection;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (removeSkills && !scope) {
|
|
464
|
+
scope = "local";
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (removeMcp && !mcpScope) {
|
|
468
|
+
mcpScope = "local";
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (!hostSelection.length) {
|
|
472
|
+
warn("No host applications selected. Uninstall requires at least one host application.");
|
|
473
|
+
process.exit(1);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (removeSkills) {
|
|
477
|
+
info(`Skills scope: ${scope === "local" ? "Local" : "Global"}`);
|
|
478
|
+
}
|
|
479
|
+
if (removeMcp) {
|
|
480
|
+
info(`MCP scope: ${mcpScope === "local" ? "Local" : "Global"}`);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (removeSkills) {
|
|
484
|
+
const skillsSpinner = startSpinner("Removing skills...");
|
|
485
|
+
try {
|
|
486
|
+
const skillTargets =
|
|
487
|
+
scope === "local"
|
|
488
|
+
? hostSelection.map((host) => hostPaths[host].localSkillsDir)
|
|
489
|
+
: hostSelection.map((host) => hostPaths[host].skillsDir);
|
|
490
|
+
|
|
491
|
+
const skillNames = config.skills.map((skill) =>
|
|
492
|
+
typeof skill === "string" ? skill : skill.npmName,
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
const result = await uninstallSkillsFromTargets(
|
|
496
|
+
skillNames,
|
|
497
|
+
skillTargets,
|
|
498
|
+
config.skillsFolder,
|
|
499
|
+
config.readmeTemplate,
|
|
500
|
+
);
|
|
501
|
+
skillsSpinner.succeed(
|
|
502
|
+
`Removed ${result.removed} skill folder(s) from ${skillTargets.length} host application location(s).`,
|
|
503
|
+
);
|
|
504
|
+
} catch (error) {
|
|
505
|
+
skillsSpinner.fail(`Failed to remove skills: ${error.message}`);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (removeMcp) {
|
|
510
|
+
const mcpSpinner = startSpinner("Removing MCP configurations...");
|
|
511
|
+
const mcpConfigPaths =
|
|
512
|
+
mcpScope === "local"
|
|
513
|
+
? hostSelection.map((host) => hostPaths[host].localMcpConfig)
|
|
514
|
+
: hostSelection.map((host) => hostPaths[host].mcpConfig);
|
|
515
|
+
|
|
516
|
+
let removedCount = 0;
|
|
517
|
+
const serverNames = config.mcpServers.map((server) => server.name);
|
|
518
|
+
|
|
519
|
+
for (let i = 0; i < hostSelection.length; i++) {
|
|
520
|
+
const host = hostSelection[i];
|
|
521
|
+
const result = await removeMcpConfig(
|
|
522
|
+
mcpConfigPaths[i],
|
|
523
|
+
serverNames,
|
|
524
|
+
hostPaths[host].mcpServerKey,
|
|
525
|
+
);
|
|
526
|
+
removedCount += result.removed;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (removedCount > 0) {
|
|
530
|
+
mcpSpinner.succeed(
|
|
531
|
+
`Removed ${removedCount} MCP entries from ${hostSelection.length} host application(s) (${mcpScope} scope).`,
|
|
532
|
+
);
|
|
533
|
+
} else {
|
|
534
|
+
mcpSpinner.succeed("No matching MCP entries found to remove.");
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
success("Uninstall complete.");
|
|
539
|
+
}
|
|
540
|
+
|
|
343
541
|
async function runGitMcpInstallation(projectRoot, platformInfo, config, hostPaths, args) {
|
|
344
542
|
// Check if --yes flag is used with --git-mcp
|
|
345
543
|
if (args.autoYes) {
|
|
@@ -535,3 +733,4 @@ async function runGitMcpInstallation(projectRoot, platformInfo, config, hostPath
|
|
|
535
733
|
}
|
|
536
734
|
|
|
537
735
|
export { run };
|
|
736
|
+
|
package/src/installers/mcp.js
CHANGED
|
@@ -49,6 +49,39 @@ function mergeServers(existing, incoming, serverKey = "servers") {
|
|
|
49
49
|
return merged;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
function removeServers(existing, removeNames, serverKey = "servers") {
|
|
53
|
+
const existingServers = existing[serverKey] && typeof existing[serverKey] === "object"
|
|
54
|
+
? existing[serverKey]
|
|
55
|
+
: null;
|
|
56
|
+
|
|
57
|
+
if (!existingServers) {
|
|
58
|
+
return { updated: existing, removed: 0 };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const updatedServers = { ...existingServers };
|
|
62
|
+
let removed = 0;
|
|
63
|
+
|
|
64
|
+
for (const name of removeNames) {
|
|
65
|
+
if (Object.prototype.hasOwnProperty.call(updatedServers, name)) {
|
|
66
|
+
delete updatedServers[name];
|
|
67
|
+
removed++;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (removed === 0) {
|
|
72
|
+
return { updated: existing, removed: 0 };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const updated = { ...existing };
|
|
76
|
+
if (Object.keys(updatedServers).length === 0) {
|
|
77
|
+
delete updated[serverKey];
|
|
78
|
+
} else {
|
|
79
|
+
updated[serverKey] = updatedServers;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { updated, removed };
|
|
83
|
+
}
|
|
84
|
+
|
|
52
85
|
async function installMcpConfig(configPath, servers, serverKey = "servers") {
|
|
53
86
|
await fs.mkdir(path.dirname(configPath), { recursive: true });
|
|
54
87
|
const existing = await loadJson(configPath);
|
|
@@ -56,6 +89,23 @@ async function installMcpConfig(configPath, servers, serverKey = "servers") {
|
|
|
56
89
|
await fs.writeFile(configPath, `${JSON.stringify(updated, null, 2)}\n`, "utf8");
|
|
57
90
|
}
|
|
58
91
|
|
|
92
|
+
async function removeMcpConfig(configPath, serverNames, serverKey = "servers") {
|
|
93
|
+
if (!(await pathExists(configPath))) {
|
|
94
|
+
return { removed: 0, changed: false };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const existing = await loadJson(configPath);
|
|
98
|
+
const { updated, removed } = removeServers(existing, serverNames, serverKey);
|
|
99
|
+
|
|
100
|
+
if (removed === 0) {
|
|
101
|
+
return { removed: 0, changed: false };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
await fs.writeFile(configPath, `${JSON.stringify(updated, null, 2)}\n`, "utf8");
|
|
105
|
+
return { removed, changed: true };
|
|
106
|
+
}
|
|
107
|
+
|
|
59
108
|
export {
|
|
60
|
-
installMcpConfig
|
|
109
|
+
installMcpConfig,
|
|
110
|
+
removeMcpConfig
|
|
61
111
|
};
|
package/src/installers/skills.js
CHANGED
|
@@ -15,6 +15,10 @@ async function pathExists(target) {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function getSkillDirName(skillPackageName) {
|
|
19
|
+
return skillPackageName.replace(/-skill$/, "");
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
function run(command, args, options = {}) {
|
|
19
23
|
return new Promise((resolve, reject) => {
|
|
20
24
|
const child = spawn(command, args, {
|
|
@@ -53,6 +57,17 @@ async function cleanupTemp(tempDir) {
|
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
|
|
60
|
+
async function removeDirIfEmpty(targetDir) {
|
|
61
|
+
if (!(await pathExists(targetDir))) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const entries = await fs.readdir(targetDir);
|
|
66
|
+
if (entries.length === 0) {
|
|
67
|
+
await fs.rmdir(targetDir);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
56
71
|
/**
|
|
57
72
|
* Install skills from npm packages into IDE skills directories.
|
|
58
73
|
*
|
|
@@ -116,7 +131,7 @@ async function installSkillsFromNpm(
|
|
|
116
131
|
|
|
117
132
|
if (await pathExists(skillMdPath)) {
|
|
118
133
|
// Create skill directory in target (use package name without -skill suffix)
|
|
119
|
-
const skillDirName =
|
|
134
|
+
const skillDirName = getSkillDirName(skill);
|
|
120
135
|
const targetSkillDir = path.join(skillsDir, skillDirName);
|
|
121
136
|
await fs.mkdir(targetSkillDir, { recursive: true });
|
|
122
137
|
|
|
@@ -146,4 +161,49 @@ async function installSkillsFromNpm(
|
|
|
146
161
|
};
|
|
147
162
|
}
|
|
148
163
|
|
|
149
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Remove skills installed by this tool from target directories.
|
|
166
|
+
*
|
|
167
|
+
* @param {string[]} skills - Array of npm package names
|
|
168
|
+
* @param {string[]} targetDirs - Array of target directories to uninstall from
|
|
169
|
+
* @param {string} skillsFolder - Optional subfolder name used for bundled skills
|
|
170
|
+
* @param {string} readmeTemplate - README template filename from templates folder
|
|
171
|
+
* @returns {Promise<{removed: number}>}
|
|
172
|
+
*/
|
|
173
|
+
async function uninstallSkillsFromTargets(
|
|
174
|
+
skills,
|
|
175
|
+
targetDirs,
|
|
176
|
+
skillsFolder = null,
|
|
177
|
+
readmeTemplate = "deploy-README.md",
|
|
178
|
+
) {
|
|
179
|
+
let removedCount = 0;
|
|
180
|
+
|
|
181
|
+
for (const targetDir of targetDirs) {
|
|
182
|
+
const skillsDir = skillsFolder
|
|
183
|
+
? path.join(targetDir, skillsFolder)
|
|
184
|
+
: targetDir;
|
|
185
|
+
|
|
186
|
+
for (const skill of skills) {
|
|
187
|
+
const skillDirName = getSkillDirName(skill);
|
|
188
|
+
const targetSkillDir = path.join(skillsDir, skillDirName);
|
|
189
|
+
|
|
190
|
+
if (await pathExists(targetSkillDir)) {
|
|
191
|
+
await fs.rm(targetSkillDir, { recursive: true, force: true });
|
|
192
|
+
removedCount++;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const readmePath = path.join(skillsDir, "a11y-devkit-README.md");
|
|
197
|
+
if (await pathExists(readmePath)) {
|
|
198
|
+
await fs.rm(readmePath, { force: true });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (skillsFolder) {
|
|
202
|
+
await removeDirIfEmpty(skillsDir);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return { removed: removedCount };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export { installSkillsFromNpm, uninstallSkillsFromTargets, cleanupTemp };
|
|
File without changes
|