a11y-devkit-deploy 0.6.5 → 0.7.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 +71 -14
- package/config/a11y.json +85 -41
- package/package.json +1 -1
- package/src/cli.js +73 -61
- package/src/installers/skills.js +50 -22
- package/src/paths.js +18 -52
- /package/templates/{skills-README.md → deploy-README.md} +0 -0
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# A11y Devkit Deploy
|
|
2
2
|
|
|
3
|
-
A cross-platform CLI for deploying accessibility skills and MCP servers across Claude Code, Cursor, Codex, and VSCode.
|
|
3
|
+
A **fully config-driven**, cross-platform CLI for deploying accessibility skills and MCP servers across Claude Code, Cursor, Codex, and VSCode.
|
|
4
|
+
|
|
5
|
+
**Add new skills or MCP servers without writing code** - just edit the JSON config and re-run.
|
|
4
6
|
|
|
5
7
|
## Install
|
|
6
8
|
|
|
@@ -61,17 +63,23 @@ All MCP servers are configured to run via `npx`, which means:
|
|
|
61
63
|
|
|
62
64
|
This CLI automates the setup of accessibility tooling by:
|
|
63
65
|
|
|
64
|
-
1. **Installing skills from npm** - Downloads and installs
|
|
65
|
-
2. **Configuring MCP servers** - Updates each IDE's MCP config to enable
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
- **
|
|
69
|
-
- **
|
|
70
|
-
|
|
66
|
+
1. **Installing skills from npm** - Downloads and installs accessibility skill packages (configurable in `config/a11y.json`)
|
|
67
|
+
2. **Configuring MCP servers** - Updates each IDE's MCP config to enable accessibility-focused MCP servers (also configurable)
|
|
68
|
+
|
|
69
|
+
**Default configuration includes:**
|
|
70
|
+
- **7 accessibility skills** - Testing, remediation, validation, documentation, and orchestration
|
|
71
|
+
- **5 MCP servers**:
|
|
72
|
+
- **wcag** - WCAG 2.2 guidelines, success criteria, and techniques
|
|
73
|
+
- **aria** - WAI-ARIA roles, states, properties, and patterns
|
|
74
|
+
- **magentaa11y** - Component accessibility acceptance criteria
|
|
75
|
+
- **a11y-personas** - Accessibility personas for diverse user needs
|
|
76
|
+
- **arc-issues** - Format AxeCore violations into standardized issue templates
|
|
71
77
|
|
|
72
|
-
|
|
78
|
+
**Fully customizable** - add/remove skills or MCP servers by editing the config file.
|
|
73
79
|
|
|
74
|
-
|
|
80
|
+
### Skills Installed (Default)
|
|
81
|
+
|
|
82
|
+
The following skill packages are installed from npm by default. **Add your own by editing `config/a11y.json`**:
|
|
75
83
|
|
|
76
84
|
| Skill | Package | Description |
|
|
77
85
|
|-------|---------|-------------|
|
|
@@ -110,11 +118,58 @@ The generated MCP config looks like this:
|
|
|
110
118
|
|
|
111
119
|
## Configuration
|
|
112
120
|
|
|
113
|
-
Edit `config/a11y.json` to customize
|
|
121
|
+
The entire tool is **fully config-driven**. Edit `config/a11y.json` to customize everything without touching code:
|
|
122
|
+
|
|
123
|
+
### Example: Adding a New Skill
|
|
124
|
+
|
|
125
|
+
Simply add an object to the `skills` array with a `name` (npm package) and `description`:
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"skills": [
|
|
130
|
+
{
|
|
131
|
+
"name": "a11y-tester-skill",
|
|
132
|
+
"description": "Run accessibility tests"
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"name": "your-custom-skill",
|
|
136
|
+
"description": "Your custom skill description"
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Example: Adding a New MCP Server
|
|
143
|
+
|
|
144
|
+
Add an object to the `mcpServers` array with name, description, command, and args:
|
|
114
145
|
|
|
115
|
-
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"mcpServers": [
|
|
149
|
+
{
|
|
150
|
+
"name": "wcag",
|
|
151
|
+
"description": "WCAG guidelines reference",
|
|
152
|
+
"command": "npx",
|
|
153
|
+
"args": ["-y", "wcag-mcp"]
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "your-custom-mcp",
|
|
157
|
+
"description": "Your custom MCP server",
|
|
158
|
+
"command": "npx",
|
|
159
|
+
"args": ["-y", "your-mcp-package"]
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Config Structure
|
|
166
|
+
|
|
167
|
+
- `skills` - Array of skill objects with `name` (npm package) and `description`
|
|
116
168
|
- `ideSkillsPaths` - IDE-specific skills directories (configurable per IDE)
|
|
117
|
-
- `
|
|
169
|
+
- `ideMcpPaths` - IDE-specific MCP config file paths
|
|
170
|
+
- `mcpServers` - MCP server definitions with name, description, command, and args
|
|
171
|
+
|
|
172
|
+
All changes take effect immediately - just re-run the CLI to deploy your updated config.
|
|
118
173
|
|
|
119
174
|
## Directory Structure
|
|
120
175
|
|
|
@@ -142,7 +197,9 @@ MCP configurations are written to each IDE's OS-specific config path:
|
|
|
142
197
|
- **Windows**: `%APPDATA%\{IDE}\mcp.json`
|
|
143
198
|
- **Linux**: `~/.config/{IDE}/mcp.json`
|
|
144
199
|
|
|
145
|
-
## MCP Servers Included
|
|
200
|
+
## MCP Servers Included (Default)
|
|
201
|
+
|
|
202
|
+
**Add your own by editing `config/a11y.json`**:
|
|
146
203
|
|
|
147
204
|
| Server | Package | Description |
|
|
148
205
|
|--------|---------|-------------|
|
package/config/a11y.json
CHANGED
|
@@ -1,66 +1,110 @@
|
|
|
1
1
|
{
|
|
2
|
+
"skillsFolder": "a11y",
|
|
3
|
+
"readmeTemplate": "deploy-README.md",
|
|
2
4
|
"skills": [
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
{
|
|
6
|
+
"name": "a11y-base-web-skill",
|
|
7
|
+
"description": "Core accessibility testing utilities"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "a11y-issue-writer-skill",
|
|
11
|
+
"description": "Document accessibility issues"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"name": "a11y-tester-skill",
|
|
15
|
+
"description": "Run accessibility tests"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "a11y-remediator-skill",
|
|
19
|
+
"description": "Fix accessibility issues"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "a11y-validator-skill",
|
|
23
|
+
"description": "Validate accessibility compliance"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "web-standards-skill",
|
|
27
|
+
"description": "Web standards reference"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "a11y-audit-fix-agent-orchestrator-skill",
|
|
31
|
+
"description": "Orchestrate accessibility audits"
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
"ides": [
|
|
35
|
+
{
|
|
36
|
+
"id": "claude",
|
|
37
|
+
"displayName": "Claude Code",
|
|
38
|
+
"mcpServerKey": "servers",
|
|
39
|
+
"skillsFolder": ".claude/skills",
|
|
40
|
+
"mcpConfigFile": ".claude/mcp.json"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "cursor",
|
|
44
|
+
"displayName": "Cursor",
|
|
45
|
+
"mcpServerKey": "mcpServers",
|
|
46
|
+
"skillsFolder": ".cursor/skills",
|
|
47
|
+
"mcpConfigFile": ".cursor/mcp.json"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "codex",
|
|
51
|
+
"displayName": "Codex",
|
|
52
|
+
"mcpServerKey": "servers",
|
|
53
|
+
"skillsFolder": ".codex/skills",
|
|
54
|
+
"mcpConfigFile": ".codex/mcp.json"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"id": "vscode",
|
|
58
|
+
"displayName": "VSCode",
|
|
59
|
+
"mcpServerKey": "servers",
|
|
60
|
+
"skillsFolder": ".github/skills",
|
|
61
|
+
"mcpConfigFile": ".github/mcp.json"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"id": "windsurf",
|
|
65
|
+
"displayName": "Windsurf",
|
|
66
|
+
"mcpServerKey": "servers",
|
|
67
|
+
"skillsFolder": ".codeium/windsurf/skills",
|
|
68
|
+
"mcpConfigFile": ".codeium/windsurf/mcp_config.json"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"id": "factory",
|
|
72
|
+
"displayName": "Factory",
|
|
73
|
+
"mcpServerKey": "mcpServers",
|
|
74
|
+
"skillsFolder": ".factory/skills",
|
|
75
|
+
"mcpConfigFile": ".factory/mcp.json"
|
|
76
|
+
}
|
|
10
77
|
],
|
|
11
|
-
"ideSkillsPaths": {
|
|
12
|
-
"claude": ".claude/skills",
|
|
13
|
-
"cursor": ".cursor/skills",
|
|
14
|
-
"codex": ".codex/skills",
|
|
15
|
-
"vscode": ".github/skills",
|
|
16
|
-
"local": ".github/skills"
|
|
17
|
-
},
|
|
18
|
-
"ideMcpPaths": {
|
|
19
|
-
"claude": ".claude/mcp.json",
|
|
20
|
-
"cursor": ".cursor/mcp.json",
|
|
21
|
-
"codex": ".codex/mcp.json",
|
|
22
|
-
"vscode": ".github/mcp.json"
|
|
23
|
-
},
|
|
24
78
|
"mcpServers": [
|
|
25
79
|
{
|
|
26
80
|
"name": "wcag",
|
|
81
|
+
"description": "WCAG guidelines reference",
|
|
27
82
|
"command": "npx",
|
|
28
|
-
"args": [
|
|
29
|
-
"-y",
|
|
30
|
-
"wcag-mcp"
|
|
31
|
-
]
|
|
83
|
+
"args": ["-y", "wcag-mcp"]
|
|
32
84
|
},
|
|
33
85
|
{
|
|
34
86
|
"name": "aria",
|
|
87
|
+
"description": "ARIA specification reference",
|
|
35
88
|
"command": "npx",
|
|
36
|
-
"args": [
|
|
37
|
-
"-y",
|
|
38
|
-
"aria-mcp"
|
|
39
|
-
]
|
|
89
|
+
"args": ["-y", "aria-mcp"]
|
|
40
90
|
},
|
|
41
91
|
{
|
|
42
92
|
"name": "magentaa11y",
|
|
93
|
+
"description": "MagentaA11y accessibility acceptance criteria tool",
|
|
43
94
|
"command": "npx",
|
|
44
|
-
"args": [
|
|
45
|
-
"-y",
|
|
46
|
-
"magentaa11y-mcp"
|
|
47
|
-
]
|
|
95
|
+
"args": ["-y", "magentaa11y-mcp"]
|
|
48
96
|
},
|
|
49
97
|
{
|
|
50
98
|
"name": "a11y-personas",
|
|
99
|
+
"description": "Accessibility personas and user scenarios",
|
|
51
100
|
"command": "npx",
|
|
52
|
-
"args": [
|
|
53
|
-
"-y",
|
|
54
|
-
"a11y-personas-mcp"
|
|
55
|
-
]
|
|
101
|
+
"args": ["-y", "a11y-personas-mcp"]
|
|
56
102
|
},
|
|
57
103
|
{
|
|
58
104
|
"name": "arc-issues",
|
|
105
|
+
"description": "Pre-formatted a11y issue templates",
|
|
59
106
|
"command": "npx",
|
|
60
|
-
"args": [
|
|
61
|
-
"-y",
|
|
62
|
-
"arc-issues-mcp"
|
|
63
|
-
]
|
|
107
|
+
"args": ["-y", "arc-issues-mcp"]
|
|
64
108
|
}
|
|
65
109
|
]
|
|
66
|
-
}
|
|
110
|
+
}
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -23,29 +23,15 @@ async function loadPackageJson() {
|
|
|
23
23
|
return JSON.parse(raw);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const skillDescriptions = {
|
|
27
|
-
"a11y-base-web-skill": "Core accessibility testing utilities",
|
|
28
|
-
"a11y-issue-writer-skill": "Document accessibility issues",
|
|
29
|
-
"a11y-tester-skill": "Run accessibility tests",
|
|
30
|
-
"a11y-remediator-skill": "Fix accessibility issues",
|
|
31
|
-
"a11y-validator-skill": "Validate accessibility compliance",
|
|
32
|
-
"web-standards-skill": "Web standards reference",
|
|
33
|
-
"a11y-audit-fix-agent-orchestrator-skill": "Orchestrate accessibility audits"
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const mcpDescriptions = {
|
|
37
|
-
"wcag": "WCAG guidelines reference",
|
|
38
|
-
"aria": "ARIA specification reference",
|
|
39
|
-
"magentaa11y": "MagentaA11y accessibility acceptance criteria tool",
|
|
40
|
-
"a11y-personas": "Accessibility personas and user scenarios",
|
|
41
|
-
"arc-issues": "Pre-formatted a11y issue templates"
|
|
42
|
-
};
|
|
43
|
-
|
|
44
26
|
function parseArgs(argv) {
|
|
45
27
|
const args = new Set(argv.slice(2));
|
|
46
28
|
return {
|
|
47
29
|
autoYes: args.has("--yes") || args.has("-y"),
|
|
48
|
-
scope: args.has("--global")
|
|
30
|
+
scope: args.has("--global")
|
|
31
|
+
? "global"
|
|
32
|
+
: args.has("--local")
|
|
33
|
+
? "local"
|
|
34
|
+
: null,
|
|
49
35
|
};
|
|
50
36
|
}
|
|
51
37
|
|
|
@@ -61,35 +47,40 @@ async function run() {
|
|
|
61
47
|
const platformInfo = getPlatform();
|
|
62
48
|
const config = await loadConfig();
|
|
63
49
|
const pkg = await loadPackageJson();
|
|
64
|
-
const idePaths = getIdePaths(projectRoot, platformInfo, config.
|
|
50
|
+
const idePaths = getIdePaths(projectRoot, platformInfo, config.ides);
|
|
65
51
|
const args = parseArgs(process.argv);
|
|
66
52
|
|
|
67
|
-
header(
|
|
53
|
+
header(
|
|
54
|
+
`A11y Devkit Deploy v${pkg.version}`,
|
|
55
|
+
"Install skills + MCP servers across IDEs",
|
|
56
|
+
);
|
|
68
57
|
info(`Detected OS: ${formatOs(platformInfo)}`);
|
|
69
58
|
|
|
70
59
|
console.log("\nSkills to install:");
|
|
71
60
|
config.skills.forEach((skill) => {
|
|
72
|
-
const
|
|
73
|
-
|
|
61
|
+
const name = typeof skill === "string" ? skill : skill.name;
|
|
62
|
+
const description =
|
|
63
|
+
typeof skill === "string"
|
|
64
|
+
? "No description"
|
|
65
|
+
: skill.description || "No description";
|
|
66
|
+
console.log(`- ${name}: ${description}`);
|
|
74
67
|
});
|
|
75
68
|
|
|
76
69
|
console.log("\nMCP Servers to install:");
|
|
77
70
|
config.mcpServers.forEach((server) => {
|
|
78
|
-
const description =
|
|
71
|
+
const description = server.description || "No description";
|
|
79
72
|
console.log(`${server.name} - ${description}`);
|
|
80
73
|
});
|
|
81
74
|
console.log("");
|
|
82
75
|
|
|
83
|
-
const ideChoices =
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
{ title: "VSCode", value: "vscode" }
|
|
88
|
-
];
|
|
76
|
+
const ideChoices = config.ides.map((ide) => ({
|
|
77
|
+
title: ide.displayName,
|
|
78
|
+
value: ide.id,
|
|
79
|
+
}));
|
|
89
80
|
|
|
90
81
|
let scope = args.scope;
|
|
91
82
|
let mcpScope = null;
|
|
92
|
-
let ideSelection =
|
|
83
|
+
let ideSelection = config.ides.map((ide) => ide.id);
|
|
93
84
|
let installSkills = true;
|
|
94
85
|
|
|
95
86
|
if (!args.autoYes) {
|
|
@@ -100,10 +91,13 @@ async function run() {
|
|
|
100
91
|
name: "scope",
|
|
101
92
|
message: "Install skills + repo locally or globally?",
|
|
102
93
|
choices: [
|
|
103
|
-
{
|
|
104
|
-
|
|
94
|
+
{
|
|
95
|
+
title: `Local to this project (${formatPath(projectRoot)})`,
|
|
96
|
+
value: "local",
|
|
97
|
+
},
|
|
98
|
+
{ title: "Global for this user", value: "global" },
|
|
105
99
|
],
|
|
106
|
-
initial: 0
|
|
100
|
+
initial: 0,
|
|
107
101
|
},
|
|
108
102
|
{
|
|
109
103
|
type: "select",
|
|
@@ -113,22 +107,23 @@ async function run() {
|
|
|
113
107
|
{
|
|
114
108
|
title: `Local to this project (${formatPath(projectRoot)})`,
|
|
115
109
|
value: "local",
|
|
116
|
-
description:
|
|
110
|
+
description:
|
|
111
|
+
"Write to project-level IDE config folders (version-controllable)",
|
|
117
112
|
},
|
|
118
113
|
{
|
|
119
114
|
title: "Global for this user",
|
|
120
115
|
value: "global",
|
|
121
|
-
description: "Write to user-level IDE config folders"
|
|
122
|
-
}
|
|
116
|
+
description: "Write to user-level IDE config folders",
|
|
117
|
+
},
|
|
123
118
|
],
|
|
124
|
-
initial: 0
|
|
119
|
+
initial: 0,
|
|
125
120
|
},
|
|
126
121
|
{
|
|
127
122
|
type: "multiselect",
|
|
128
123
|
name: "ides",
|
|
129
124
|
message: "Configure MCP for which IDEs?",
|
|
130
125
|
choices: ideChoices,
|
|
131
|
-
initial: ideChoices.map((_, index) => index)
|
|
126
|
+
initial: ideChoices.map((_, index) => index),
|
|
132
127
|
},
|
|
133
128
|
{
|
|
134
129
|
type: "toggle",
|
|
@@ -136,15 +131,15 @@ async function run() {
|
|
|
136
131
|
message: "Install skills into IDE skills folders?",
|
|
137
132
|
active: "yes",
|
|
138
133
|
inactive: "no",
|
|
139
|
-
initial: true
|
|
140
|
-
}
|
|
134
|
+
initial: true,
|
|
135
|
+
},
|
|
141
136
|
],
|
|
142
137
|
{
|
|
143
138
|
onCancel: () => {
|
|
144
139
|
warn("Setup cancelled.");
|
|
145
140
|
process.exit(0);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
141
|
+
},
|
|
142
|
+
},
|
|
148
143
|
);
|
|
149
144
|
|
|
150
145
|
scope = scope || response.scope;
|
|
@@ -175,12 +170,24 @@ async function run() {
|
|
|
175
170
|
const skillsSpinner = startSpinner("Installing skills from npm...");
|
|
176
171
|
|
|
177
172
|
try {
|
|
178
|
-
const skillTargets =
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
173
|
+
const skillTargets =
|
|
174
|
+
scope === "local"
|
|
175
|
+
? ideSelection.map((ide) => idePaths[ide].localSkillsDir)
|
|
176
|
+
: ideSelection.map((ide) => idePaths[ide].skillsDir);
|
|
177
|
+
|
|
178
|
+
const skillNames = config.skills.map((skill) =>
|
|
179
|
+
typeof skill === "string" ? skill : skill.name,
|
|
180
|
+
);
|
|
181
|
+
const result = await installSkillsFromNpm(
|
|
182
|
+
skillNames,
|
|
183
|
+
skillTargets,
|
|
184
|
+
tempDir,
|
|
185
|
+
config.skillsFolder,
|
|
186
|
+
config.readmeTemplate,
|
|
187
|
+
);
|
|
188
|
+
skillsSpinner.succeed(
|
|
189
|
+
`${result.installed} skills installed to ${skillTargets.length} IDE location(s).`,
|
|
190
|
+
);
|
|
184
191
|
} catch (error) {
|
|
185
192
|
skillsSpinner.fail(`Failed to install skills: ${error.message}`);
|
|
186
193
|
}
|
|
@@ -190,19 +197,22 @@ async function run() {
|
|
|
190
197
|
|
|
191
198
|
// Configure MCP servers using npx (no local installation needed!)
|
|
192
199
|
const mcpSpinner = startSpinner("Updating MCP configurations...");
|
|
193
|
-
const mcpConfigPaths =
|
|
194
|
-
|
|
195
|
-
|
|
200
|
+
const mcpConfigPaths =
|
|
201
|
+
mcpScope === "local"
|
|
202
|
+
? ideSelection.map((ide) => idePaths[ide].localMcpConfig)
|
|
203
|
+
: ideSelection.map((ide) => idePaths[ide].mcpConfig);
|
|
196
204
|
|
|
197
205
|
for (let i = 0; i < ideSelection.length; i++) {
|
|
198
206
|
const ide = ideSelection[i];
|
|
199
207
|
await installMcpConfig(
|
|
200
208
|
mcpConfigPaths[i],
|
|
201
209
|
config.mcpServers,
|
|
202
|
-
idePaths[ide].mcpServerKey
|
|
210
|
+
idePaths[ide].mcpServerKey,
|
|
203
211
|
);
|
|
204
212
|
}
|
|
205
|
-
mcpSpinner.succeed(
|
|
213
|
+
mcpSpinner.succeed(
|
|
214
|
+
`MCP configs updated for ${ideSelection.length} IDE(s) (${mcpScope} scope).`,
|
|
215
|
+
);
|
|
206
216
|
|
|
207
217
|
// Clean up temporary directory
|
|
208
218
|
const cleanupSpinner = startSpinner("Cleaning up temporary files...");
|
|
@@ -214,17 +224,19 @@ async function run() {
|
|
|
214
224
|
info("MCP servers use npx - no local installation needed!");
|
|
215
225
|
console.log("");
|
|
216
226
|
success("Next Steps:");
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
227
|
+
const skillsFolderPath = config.skillsFolder ? `${config.skillsFolder}/` : "";
|
|
228
|
+
const skillsPath =
|
|
229
|
+
scope === "local"
|
|
230
|
+
? `.claude/skills/${skillsFolderPath}README.md (or your IDE's equivalent)`
|
|
231
|
+
: `~/.claude/skills/${skillsFolderPath}README.md (or your IDE's global skills directory)`;
|
|
220
232
|
info(`📖 Check ${skillsPath} for comprehensive usage guide`);
|
|
221
233
|
info("✨ Includes 70+ example prompts for all skills and MCP servers");
|
|
222
|
-
info(
|
|
234
|
+
info(
|
|
235
|
+
"🚀 Start with the 'Getting Started' section for your first accessibility check",
|
|
236
|
+
);
|
|
223
237
|
console.log("");
|
|
224
238
|
info("You can re-run this CLI any time to update skills and configs.");
|
|
225
239
|
info("Documentation: https://github.com/joe-watkins/a11y-devkit#readme");
|
|
226
240
|
}
|
|
227
241
|
|
|
228
|
-
export {
|
|
229
|
-
run
|
|
230
|
-
};
|
|
242
|
+
export { run };
|
package/src/installers/skills.js
CHANGED
|
@@ -17,20 +17,32 @@ async function pathExists(target) {
|
|
|
17
17
|
|
|
18
18
|
function run(command, args, options = {}) {
|
|
19
19
|
return new Promise((resolve, reject) => {
|
|
20
|
-
const child = spawn(command, args, {
|
|
20
|
+
const child = spawn(command, args, {
|
|
21
|
+
stdio: "pipe",
|
|
22
|
+
shell: true,
|
|
23
|
+
...options,
|
|
24
|
+
});
|
|
21
25
|
let stdout = "";
|
|
22
26
|
let stderr = "";
|
|
23
|
-
|
|
24
|
-
child.stdout?.on("data", (data) => {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
|
|
28
|
+
child.stdout?.on("data", (data) => {
|
|
29
|
+
stdout += data;
|
|
30
|
+
});
|
|
31
|
+
child.stderr?.on("data", (data) => {
|
|
32
|
+
stderr += data;
|
|
33
|
+
});
|
|
34
|
+
|
|
27
35
|
child.on("error", reject);
|
|
28
36
|
child.on("close", (code) => {
|
|
29
37
|
if (code === 0) {
|
|
30
38
|
resolve({ stdout, stderr });
|
|
31
39
|
return;
|
|
32
40
|
}
|
|
33
|
-
reject(
|
|
41
|
+
reject(
|
|
42
|
+
new Error(
|
|
43
|
+
`${command} ${args.join(" ")} failed with code ${code}: ${stderr}`,
|
|
44
|
+
),
|
|
45
|
+
);
|
|
34
46
|
});
|
|
35
47
|
});
|
|
36
48
|
}
|
|
@@ -43,18 +55,26 @@ async function cleanupTemp(tempDir) {
|
|
|
43
55
|
|
|
44
56
|
/**
|
|
45
57
|
* Install skills from npm packages into IDE skills directories.
|
|
46
|
-
*
|
|
58
|
+
*
|
|
47
59
|
* 1. Creates temp directory with package.json listing skills as dependencies
|
|
48
60
|
* 2. Runs npm install in temp directory
|
|
49
61
|
* 3. Copies installed skill packages (SKILL.md files) to target directories
|
|
50
62
|
* 4. Returns temp directory path for cleanup
|
|
51
|
-
*
|
|
63
|
+
*
|
|
52
64
|
* @param {string[]} skills - Array of npm package names
|
|
53
65
|
* @param {string[]} targetDirs - Array of target directories to install skills to
|
|
54
66
|
* @param {string} tempDir - Temporary directory for npm install
|
|
67
|
+
* @param {string} skillsFolder - Optional subfolder name to bundle skills (e.g., "a11y")
|
|
68
|
+
* @param {string} readmeTemplate - README template filename from templates folder
|
|
55
69
|
* @returns {Promise<{installed: number, tempDir: string}>}
|
|
56
70
|
*/
|
|
57
|
-
async function installSkillsFromNpm(
|
|
71
|
+
async function installSkillsFromNpm(
|
|
72
|
+
skills,
|
|
73
|
+
targetDirs,
|
|
74
|
+
tempDir,
|
|
75
|
+
skillsFolder = null,
|
|
76
|
+
readmeTemplate = "deploy-README.md",
|
|
77
|
+
) {
|
|
58
78
|
// Create temp directory
|
|
59
79
|
await fs.mkdir(tempDir, { recursive: true });
|
|
60
80
|
|
|
@@ -63,7 +83,7 @@ async function installSkillsFromNpm(skills, targetDirs, tempDir) {
|
|
|
63
83
|
name: "a11y-skills-temp",
|
|
64
84
|
version: "1.0.0",
|
|
65
85
|
private: true,
|
|
66
|
-
dependencies: {}
|
|
86
|
+
dependencies: {},
|
|
67
87
|
};
|
|
68
88
|
|
|
69
89
|
for (const skill of skills) {
|
|
@@ -72,7 +92,7 @@ async function installSkillsFromNpm(skills, targetDirs, tempDir) {
|
|
|
72
92
|
|
|
73
93
|
await fs.writeFile(
|
|
74
94
|
path.join(tempDir, "package.json"),
|
|
75
|
-
JSON.stringify(packageJson, null, 2)
|
|
95
|
+
JSON.stringify(packageJson, null, 2),
|
|
76
96
|
);
|
|
77
97
|
|
|
78
98
|
// Run npm install
|
|
@@ -83,7 +103,12 @@ async function installSkillsFromNpm(skills, targetDirs, tempDir) {
|
|
|
83
103
|
let installedCount = 0;
|
|
84
104
|
|
|
85
105
|
for (const targetDir of targetDirs) {
|
|
86
|
-
|
|
106
|
+
// Determine the actual skills directory (with or without bundle folder)
|
|
107
|
+
const skillsDir = skillsFolder
|
|
108
|
+
? path.join(targetDir, skillsFolder)
|
|
109
|
+
: targetDir;
|
|
110
|
+
|
|
111
|
+
await fs.mkdir(skillsDir, { recursive: true });
|
|
87
112
|
|
|
88
113
|
for (const skill of skills) {
|
|
89
114
|
const skillPackageDir = path.join(nodeModulesDir, skill);
|
|
@@ -92,7 +117,7 @@ async function installSkillsFromNpm(skills, targetDirs, tempDir) {
|
|
|
92
117
|
if (await pathExists(skillMdPath)) {
|
|
93
118
|
// Create skill directory in target (use package name without -skill suffix)
|
|
94
119
|
const skillDirName = skill.replace(/-skill$/, "");
|
|
95
|
-
const targetSkillDir = path.join(
|
|
120
|
+
const targetSkillDir = path.join(skillsDir, skillDirName);
|
|
96
121
|
await fs.mkdir(targetSkillDir, { recursive: true });
|
|
97
122
|
|
|
98
123
|
// Copy SKILL.md
|
|
@@ -102,20 +127,23 @@ async function installSkillsFromNpm(skills, targetDirs, tempDir) {
|
|
|
102
127
|
}
|
|
103
128
|
|
|
104
129
|
// Copy the comprehensive README template to the skills directory
|
|
105
|
-
const readmeTemplatePath = path.join(
|
|
106
|
-
|
|
130
|
+
const readmeTemplatePath = path.join(
|
|
131
|
+
__dirname,
|
|
132
|
+
"..",
|
|
133
|
+
"..",
|
|
134
|
+
"templates",
|
|
135
|
+
readmeTemplate,
|
|
136
|
+
);
|
|
137
|
+
const targetReadmePath = path.join(skillsDir, "a11y-devkit-README.md");
|
|
107
138
|
if (await pathExists(readmeTemplatePath)) {
|
|
108
139
|
await fs.copyFile(readmeTemplatePath, targetReadmePath);
|
|
109
140
|
}
|
|
110
141
|
}
|
|
111
142
|
|
|
112
|
-
return {
|
|
113
|
-
installed: installedCount / targetDirs.length,
|
|
114
|
-
tempDir
|
|
143
|
+
return {
|
|
144
|
+
installed: installedCount / targetDirs.length,
|
|
145
|
+
tempDir,
|
|
115
146
|
};
|
|
116
147
|
}
|
|
117
148
|
|
|
118
|
-
export {
|
|
119
|
-
installSkillsFromNpm,
|
|
120
|
-
cleanupTemp
|
|
121
|
-
};
|
|
149
|
+
export { installSkillsFromNpm, cleanupTemp };
|
package/src/paths.js
CHANGED
|
@@ -30,60 +30,26 @@ function getAppSupportDir(platformInfo = getPlatform()) {
|
|
|
30
30
|
return process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function getIdePaths(projectRoot, platformInfo = getPlatform(),
|
|
34
|
-
const appSupport = getAppSupportDir(platformInfo);
|
|
33
|
+
function getIdePaths(projectRoot, platformInfo = getPlatform(), ideConfigs = []) {
|
|
35
34
|
const home = os.homedir();
|
|
35
|
+
const paths = {};
|
|
36
|
+
|
|
37
|
+
for (const ide of ideConfigs) {
|
|
38
|
+
// Use custom paths from config, or fall back to default pattern: ~/.{id}/
|
|
39
|
+
const skillsFolder = ide.skillsFolder || `.${ide.id}/skills`;
|
|
40
|
+
const mcpConfigFile = ide.mcpConfigFile || `.${ide.id}/mcp.json`;
|
|
41
|
+
|
|
42
|
+
paths[ide.id] = {
|
|
43
|
+
name: ide.displayName,
|
|
44
|
+
mcpConfig: path.join(home, mcpConfigFile),
|
|
45
|
+
localMcpConfig: path.join(projectRoot, mcpConfigFile),
|
|
46
|
+
mcpServerKey: ide.mcpServerKey,
|
|
47
|
+
skillsDir: path.join(home, skillsFolder),
|
|
48
|
+
localSkillsDir: path.join(projectRoot, skillsFolder)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
36
51
|
|
|
37
|
-
|
|
38
|
-
const skillsPaths = ideSkillsPaths || {
|
|
39
|
-
claude: ".claude/skills",
|
|
40
|
-
cursor: ".cursor/skills",
|
|
41
|
-
codex: ".codex/skills",
|
|
42
|
-
vscode: ".vscode/skills",
|
|
43
|
-
local: ".github/skills"
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
const mcpPaths = ideMcpPaths || {
|
|
47
|
-
claude: ".claude/mcp.json",
|
|
48
|
-
cursor: ".cursor/mcp.json",
|
|
49
|
-
codex: ".codex/mcp.json",
|
|
50
|
-
vscode: ".vscode/mcp.json"
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
claude: {
|
|
55
|
-
name: "Claude Code",
|
|
56
|
-
mcpConfig: path.join(appSupport, "Claude", "mcp.json"),
|
|
57
|
-
localMcpConfig: path.join(projectRoot, mcpPaths.claude),
|
|
58
|
-
mcpServerKey: "servers",
|
|
59
|
-
skillsDir: path.join(home, skillsPaths.claude),
|
|
60
|
-
localSkillsDir: path.join(projectRoot, skillsPaths.claude)
|
|
61
|
-
},
|
|
62
|
-
cursor: {
|
|
63
|
-
name: "Cursor",
|
|
64
|
-
mcpConfig: path.join(appSupport, "Cursor", "mcp.json"),
|
|
65
|
-
localMcpConfig: path.join(projectRoot, mcpPaths.cursor),
|
|
66
|
-
mcpServerKey: "mcpServers",
|
|
67
|
-
skillsDir: path.join(home, skillsPaths.cursor),
|
|
68
|
-
localSkillsDir: path.join(projectRoot, skillsPaths.cursor)
|
|
69
|
-
},
|
|
70
|
-
codex: {
|
|
71
|
-
name: "Codex",
|
|
72
|
-
mcpConfig: path.join(home, ".codex", "mcp.json"),
|
|
73
|
-
localMcpConfig: path.join(projectRoot, mcpPaths.codex),
|
|
74
|
-
mcpServerKey: "servers",
|
|
75
|
-
skillsDir: path.join(home, skillsPaths.codex),
|
|
76
|
-
localSkillsDir: path.join(projectRoot, skillsPaths.codex)
|
|
77
|
-
},
|
|
78
|
-
vscode: {
|
|
79
|
-
name: "VSCode",
|
|
80
|
-
mcpConfig: path.join(appSupport, "Code", "User", "mcp.json"),
|
|
81
|
-
localMcpConfig: path.join(projectRoot, mcpPaths.vscode),
|
|
82
|
-
mcpServerKey: "servers",
|
|
83
|
-
skillsDir: path.join(home, skillsPaths.vscode),
|
|
84
|
-
localSkillsDir: path.join(projectRoot, skillsPaths.vscode)
|
|
85
|
-
}
|
|
86
|
-
};
|
|
52
|
+
return paths;
|
|
87
53
|
}
|
|
88
54
|
|
|
89
55
|
export {
|
|
File without changes
|