wordpress-agent-kit 0.4.0 → 0.5.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/.agents/skills/blueprint/SKILL.md +418 -0
- package/.agents/skills/wordpress-router/SKILL.md +52 -0
- package/.agents/skills/wordpress-router/references/decision-tree.md +55 -0
- package/.agents/skills/wp-abilities-api/SKILL.md +108 -0
- package/.agents/skills/wp-abilities-api/references/delegate-helper-pattern.md +241 -0
- package/.agents/skills/wp-abilities-api/references/domain-vs-projection.md +113 -0
- package/.agents/skills/wp-abilities-api/references/error-code-vocabulary.md +123 -0
- package/.agents/skills/wp-abilities-api/references/grouping-heuristic.md +89 -0
- package/.agents/skills/wp-abilities-api/references/input-schema-gotchas.md +265 -0
- package/.agents/skills/wp-abilities-api/references/php-registration.md +94 -0
- package/.agents/skills/wp-abilities-api/references/plugin-family-patterns.md +233 -0
- package/.agents/skills/wp-abilities-api/references/rest-api.md +13 -0
- package/.agents/skills/wp-abilities-api/references/shared-core-service.md +184 -0
- package/.agents/skills/wp-abilities-audit/SKILL.md +199 -0
- package/.agents/skills/wp-abilities-audit/references/audit-schema.md +300 -0
- package/.agents/skills/wp-abilities-audit/references/capability-gate-tracing.md +197 -0
- package/.agents/skills/wp-abilities-audit/references/controller-enumeration.md +116 -0
- package/.agents/skills/wp-abilities-verify/SKILL.md +215 -0
- package/.agents/skills/wp-abilities-verify/references/annotation-correctness.md +154 -0
- package/.agents/skills/wp-abilities-verify/references/audit-schema-validation.md +131 -0
- package/.agents/skills/wp-abilities-verify/references/permission-roundtrip.md +190 -0
- package/.agents/skills/wp-abilities-verify/references/runtime-harness.md +462 -0
- package/.agents/skills/wp-abilities-verify/references/schema-lints.md +118 -0
- package/.agents/skills/wp-abilities-verify/references/static-enumeration.md +126 -0
- package/.agents/skills/wp-block-development/SKILL.md +175 -0
- package/.agents/skills/wp-block-development/references/attributes-and-serialization.md +22 -0
- package/.agents/skills/wp-block-development/references/block-json.md +49 -0
- package/.agents/skills/wp-block-development/references/creating-new-blocks.md +46 -0
- package/.agents/skills/wp-block-development/references/debugging.md +36 -0
- package/.agents/skills/wp-block-development/references/deprecations.md +24 -0
- package/.agents/skills/wp-block-development/references/dynamic-rendering.md +23 -0
- package/.agents/skills/wp-block-development/references/inner-blocks.md +25 -0
- package/.agents/skills/wp-block-development/references/registration.md +30 -0
- package/.agents/skills/wp-block-development/references/supports-and-wrappers.md +18 -0
- package/.agents/skills/wp-block-development/references/tooling-and-testing.md +21 -0
- package/.agents/skills/wp-block-development/scripts/list_blocks.mjs +121 -0
- package/.agents/skills/wp-block-themes/SKILL.md +117 -0
- package/.agents/skills/wp-block-themes/references/creating-new-block-theme.md +37 -0
- package/.agents/skills/wp-block-themes/references/debugging.md +24 -0
- package/.agents/skills/wp-block-themes/references/patterns.md +18 -0
- package/.agents/skills/wp-block-themes/references/style-variations.md +14 -0
- package/.agents/skills/wp-block-themes/references/templates-and-parts.md +16 -0
- package/.agents/skills/wp-block-themes/references/theme-json.md +59 -0
- package/.agents/skills/wp-block-themes/scripts/detect_block_themes.mjs +117 -0
- package/.agents/skills/wp-interactivity-api/SKILL.md +180 -0
- package/.agents/skills/wp-interactivity-api/references/debugging.md +29 -0
- package/.agents/skills/wp-interactivity-api/references/directives-quickref.md +30 -0
- package/.agents/skills/wp-interactivity-api/references/server-side-rendering.md +310 -0
- package/.agents/skills/wp-performance/SKILL.md +147 -0
- package/.agents/skills/wp-performance/references/autoload-options.md +24 -0
- package/.agents/skills/wp-performance/references/cron.md +20 -0
- package/.agents/skills/wp-performance/references/database.md +20 -0
- package/.agents/skills/wp-performance/references/http-api.md +15 -0
- package/.agents/skills/wp-performance/references/measurement.md +21 -0
- package/.agents/skills/wp-performance/references/object-cache.md +24 -0
- package/.agents/skills/wp-performance/references/query-monitor-headless.md +38 -0
- package/.agents/skills/wp-performance/references/server-timing.md +22 -0
- package/.agents/skills/wp-performance/references/wp-cli-doctor.md +24 -0
- package/.agents/skills/wp-performance/references/wp-cli-profile.md +32 -0
- package/.agents/skills/wp-performance/scripts/perf_inspect.mjs +128 -0
- package/.agents/skills/wp-phpstan/SKILL.md +98 -0
- package/.agents/skills/wp-phpstan/references/configuration.md +52 -0
- package/.agents/skills/wp-phpstan/references/third-party-classes.md +76 -0
- package/.agents/skills/wp-phpstan/references/wordpress-annotations.md +124 -0
- package/.agents/skills/wp-phpstan/scripts/phpstan_inspect.mjs +263 -0
- package/.agents/skills/wp-playground/SKILL.md +233 -0
- package/.agents/skills/wp-playground/references/blueprints.md +36 -0
- package/.agents/skills/wp-playground/references/cli-commands.md +39 -0
- package/.agents/skills/wp-playground/references/debugging.md +16 -0
- package/.agents/skills/wp-playground/references/e2e-playwright.md +115 -0
- package/.agents/skills/wp-plugin-development/SKILL.md +113 -0
- package/.agents/skills/wp-plugin-development/references/data-and-cron.md +19 -0
- package/.agents/skills/wp-plugin-development/references/debugging.md +19 -0
- package/.agents/skills/wp-plugin-development/references/lifecycle.md +33 -0
- package/.agents/skills/wp-plugin-development/references/security.md +29 -0
- package/.agents/skills/wp-plugin-development/references/settings-api.md +22 -0
- package/.agents/skills/wp-plugin-development/references/structure.md +16 -0
- package/.agents/skills/wp-plugin-development/scripts/detect_plugins.mjs +122 -0
- package/.agents/skills/wp-plugin-directory-guidelines/SKILL.md +133 -0
- package/.agents/skills/wp-plugin-directory-guidelines/references/gpl-compliance.md +217 -0
- package/.agents/skills/wp-plugin-directory-guidelines/references/guideline-review-checklist.md +592 -0
- package/.agents/skills/wp-plugin-directory-guidelines/references/naming-rules.md +121 -0
- package/.agents/skills/wp-project-triage/SKILL.md +39 -0
- package/.agents/skills/wp-project-triage/references/triage.schema.json +143 -0
- package/.agents/skills/wp-project-triage/scripts/detect_wp_project.mjs +610 -0
- package/.agents/skills/wp-rest-api/SKILL.md +115 -0
- package/.agents/skills/wp-rest-api/references/authentication.md +18 -0
- package/.agents/skills/wp-rest-api/references/custom-content-types.md +20 -0
- package/.agents/skills/wp-rest-api/references/discovery-and-params.md +20 -0
- package/.agents/skills/wp-rest-api/references/responses-and-fields.md +30 -0
- package/.agents/skills/wp-rest-api/references/routes-and-endpoints.md +36 -0
- package/.agents/skills/wp-rest-api/references/schema.md +22 -0
- package/.agents/skills/wp-wpcli-and-ops/SKILL.md +126 -0
- package/.agents/skills/wp-wpcli-and-ops/references/automation.md +30 -0
- package/.agents/skills/wp-wpcli-and-ops/references/cron-and-cache.md +23 -0
- package/.agents/skills/wp-wpcli-and-ops/references/debugging.md +17 -0
- package/.agents/skills/wp-wpcli-and-ops/references/multisite.md +22 -0
- package/.agents/skills/wp-wpcli-and-ops/references/packages-and-updates.md +22 -0
- package/.agents/skills/wp-wpcli-and-ops/references/safety.md +30 -0
- package/.agents/skills/wp-wpcli-and-ops/references/search-replace.md +40 -0
- package/.agents/skills/wp-wpcli-and-ops/scripts/wpcli_inspect.mjs +90 -0
- package/.agents/skills/wp-wpengine/SKILL.md +398 -0
- package/.agents/skills/wp-wpengine/references/ci-gate.md +469 -0
- package/.agents/skills/wp-wpengine/references/github-actions-deploy.md +736 -0
- package/.agents/skills/wp-wpengine/scripts/ci-gate.sh +118 -0
- package/.agents/skills/wp-wpengine/scripts/wpe-check.sh +89 -0
- package/.agents/skills/wp-wpengine/scripts/wpe-preflight.sh +104 -0
- package/.agents/skills/wpds/SKILL.md +59 -0
- package/.github/agents/wp-architect.agent.md +1 -2
- package/.github/copilot-instructions.md +1 -1
- package/.github/instructions/wordpress-workflow.instructions.md +3 -3
- package/AGENTS.md +22 -10
- package/AGENTS.template.md +20 -10
- package/README.md +89 -85
- package/dist/cli.js +5 -1
- package/dist/commands/clean-skills.js +64 -0
- package/dist/commands/setup.js +6 -2
- package/dist/commands/sync-skills.js +3 -0
- package/dist/lib/api.js +164 -5
- package/dist/lib/installer.js +166 -2
- package/extensions/wp-agent-kit/index.ts +185 -10
- package/package.json +10 -14
- package/skills-custom/wp-wpengine/SKILL.md +299 -28
- package/skills-custom/wp-wpengine/references/ci-gate.md +469 -0
- package/skills-custom/wp-wpengine/references/github-actions-deploy.md +736 -0
- package/skills-custom/wp-wpengine/scripts/ci-gate.sh +118 -0
- package/skills-custom/wp-wpengine/scripts/wpe-check.sh +89 -0
- package/skills-custom/wp-wpengine/scripts/wpe-preflight.sh +104 -0
- package/.github/workflows/ci.yml +0 -44
- package/.husky/pre-commit +0 -7
- package/CLI_REVIEW.md +0 -250
- package/biome.json +0 -39
package/dist/lib/installer.js
CHANGED
|
@@ -12,10 +12,78 @@ export const PLATFORM_FOLDERS = {
|
|
|
12
12
|
agent: '.agent',
|
|
13
13
|
pi: '.pi/agent',
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Copy universal skills from .agents/skills/ to the target directory.
|
|
17
|
+
* Uses the AgentSkills.io convention (.agents/skills/) which is discovered
|
|
18
|
+
* automatically by Pi, GitHub Copilot, and other agents.
|
|
19
|
+
*/
|
|
20
|
+
function copyUniversalSkills(targetDir) {
|
|
21
|
+
const sourceSkillsDir = path.join(PACKAGE_ROOT, '.agents', 'skills');
|
|
22
|
+
const targetSkillsDir = path.join(targetDir, '.agents', 'skills');
|
|
23
|
+
const copied = [];
|
|
24
|
+
const skipped = [];
|
|
25
|
+
if (!fs.existsSync(sourceSkillsDir)) {
|
|
26
|
+
return { copied, skipped };
|
|
27
|
+
}
|
|
28
|
+
if (!fs.existsSync(targetSkillsDir)) {
|
|
29
|
+
fs.mkdirSync(targetSkillsDir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
for (const skillName of fs.readdirSync(sourceSkillsDir)) {
|
|
32
|
+
const src = path.join(sourceSkillsDir, skillName);
|
|
33
|
+
if (!fs.statSync(src).isDirectory())
|
|
34
|
+
continue;
|
|
35
|
+
const dest = path.join(targetSkillsDir, skillName);
|
|
36
|
+
if (fs.existsSync(dest)) {
|
|
37
|
+
skipped.push(skillName);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
41
|
+
copied.push(skillName);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return { copied, skipped };
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Merge custom skills from skills-custom/ into the target's universal skills directory.
|
|
48
|
+
* Only copies skills that don't already exist in the target (avoids overwriting
|
|
49
|
+
* upstream versions that may have been user-modified).
|
|
50
|
+
* Returns arrays of skill names that were merged or skipped.
|
|
51
|
+
*/
|
|
52
|
+
function mergeCustomSkills(targetDir) {
|
|
53
|
+
const customSkillsDir = path.join(PACKAGE_ROOT, 'skills-custom');
|
|
54
|
+
if (!fs.existsSync(customSkillsDir)) {
|
|
55
|
+
return { merged: [], skipped: [] };
|
|
56
|
+
}
|
|
57
|
+
const targetSkills = path.join(targetDir, '.agents', 'skills');
|
|
58
|
+
const merged = [];
|
|
59
|
+
const skipped = [];
|
|
60
|
+
for (const skillName of fs.readdirSync(customSkillsDir)) {
|
|
61
|
+
const src = path.join(customSkillsDir, skillName);
|
|
62
|
+
if (!fs.statSync(src).isDirectory())
|
|
63
|
+
continue;
|
|
64
|
+
const dest = path.join(targetSkills, skillName);
|
|
65
|
+
if (fs.existsSync(dest)) {
|
|
66
|
+
// Don't overwrite — upstream or user version takes priority
|
|
67
|
+
skipped.push(skillName);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
71
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
72
|
+
merged.push(skillName);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { merged, skipped };
|
|
76
|
+
}
|
|
15
77
|
/**
|
|
16
78
|
* Installs the WordPress Agent Kit into the specified directory for a given platform.
|
|
17
79
|
* If the kit is already installed, uses safe update logic to preserve user modifications.
|
|
18
80
|
*
|
|
81
|
+
* Installation layout:
|
|
82
|
+
* - .agents/skills/ — Universal skills (AgentSkills.io convention)
|
|
83
|
+
* - .github/ (or .pi/agent/, etc.) — Platform-specific agents, instructions, prompts
|
|
84
|
+
* - AGENTS.md — Project-level agent instructions
|
|
85
|
+
* - AGENTS.template.md — Template reference for future updates
|
|
86
|
+
*
|
|
19
87
|
* @param targetDir - The directory where the kit should be installed.
|
|
20
88
|
* @param platform - The target platform (github, cursor, claude, agent, pi)
|
|
21
89
|
* @returns InstallKitResult with details of what was created/skipped
|
|
@@ -31,6 +99,49 @@ export function installKit(targetDir, platform = 'github', options = {}) {
|
|
|
31
99
|
// Fresh install or fallback to full replacement
|
|
32
100
|
return fullInstall(targetDir, platform, force);
|
|
33
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Migrate legacy skill directories to the universal .agents/skills/ convention.
|
|
104
|
+
* Detects skills in platform-specific directories (e.g., .github/skills/, .pi/agent/skills/)
|
|
105
|
+
* and moves them to .agents/skills/ if they don't already exist there.
|
|
106
|
+
* Returns lists of migrated and removed skill names.
|
|
107
|
+
*/
|
|
108
|
+
function migrateLegacySkills(targetDir, platform) {
|
|
109
|
+
const platformFolder = PLATFORM_FOLDERS[platform];
|
|
110
|
+
const legacySkillsDir = path.join(targetDir, platformFolder, 'skills');
|
|
111
|
+
const universalSkillsDir = path.join(targetDir, '.agents', 'skills');
|
|
112
|
+
const migrated = [];
|
|
113
|
+
const removed = [];
|
|
114
|
+
if (!fs.existsSync(legacySkillsDir)) {
|
|
115
|
+
return { migrated, removed };
|
|
116
|
+
}
|
|
117
|
+
// Ensure universal directory exists
|
|
118
|
+
if (!fs.existsSync(universalSkillsDir)) {
|
|
119
|
+
fs.mkdirSync(universalSkillsDir, { recursive: true });
|
|
120
|
+
}
|
|
121
|
+
for (const skillName of fs.readdirSync(legacySkillsDir)) {
|
|
122
|
+
const srcPath = path.join(legacySkillsDir, skillName);
|
|
123
|
+
const destPath = path.join(universalSkillsDir, skillName);
|
|
124
|
+
if (!fs.statSync(srcPath).isDirectory())
|
|
125
|
+
continue;
|
|
126
|
+
if (!fs.existsSync(destPath)) {
|
|
127
|
+
// Migrate: copy to universal location
|
|
128
|
+
fs.cpSync(srcPath, destPath, { recursive: true });
|
|
129
|
+
migrated.push(skillName);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// Already exists in universal location — just remove from legacy
|
|
133
|
+
removed.push(skillName);
|
|
134
|
+
}
|
|
135
|
+
// Remove from legacy location
|
|
136
|
+
fs.rmSync(srcPath, { recursive: true, force: true });
|
|
137
|
+
}
|
|
138
|
+
// Remove the legacy skills directory if empty
|
|
139
|
+
const remaining = fs.readdirSync(legacySkillsDir);
|
|
140
|
+
if (remaining.length === 0) {
|
|
141
|
+
fs.rmSync(legacySkillsDir, { recursive: true, force: true });
|
|
142
|
+
}
|
|
143
|
+
return { migrated, removed };
|
|
144
|
+
}
|
|
34
145
|
/**
|
|
35
146
|
* Check if kit is already installed for a platform.
|
|
36
147
|
*/
|
|
@@ -53,18 +164,23 @@ function fullInstall(targetDir, platform, _force) {
|
|
|
53
164
|
const sourceGithub = path.join(PACKAGE_ROOT, '.github');
|
|
54
165
|
const filesCreated = [];
|
|
55
166
|
const filesSkipped = [];
|
|
56
|
-
// Copy platform-specific folder
|
|
167
|
+
// Copy platform-specific folder (agents, instructions — NOT skills)
|
|
57
168
|
const targetPlatform = path.join(targetDir, platformFolder);
|
|
58
169
|
if (fs.existsSync(targetPlatform)) {
|
|
59
170
|
fs.rmSync(targetPlatform, { recursive: true, force: true });
|
|
60
171
|
}
|
|
61
172
|
if (fs.existsSync(sourceGithub)) {
|
|
62
173
|
fs.cpSync(sourceGithub, targetPlatform, { recursive: true });
|
|
174
|
+
// Remove skills from platform dir — skills go to .agents/skills/ instead
|
|
175
|
+
const platformSkills = path.join(targetPlatform, 'skills');
|
|
176
|
+
if (fs.existsSync(platformSkills)) {
|
|
177
|
+
fs.rmSync(platformSkills, { recursive: true, force: true });
|
|
178
|
+
}
|
|
63
179
|
}
|
|
64
180
|
else {
|
|
65
181
|
throw new Error('Could not find source .github directory.');
|
|
66
182
|
}
|
|
67
|
-
// Collect created files
|
|
183
|
+
// Collect created files (excluding skills which are in .agents/)
|
|
68
184
|
const collectFiles = (dir, prefix) => {
|
|
69
185
|
if (!fs.existsSync(dir))
|
|
70
186
|
return;
|
|
@@ -81,6 +197,30 @@ function fullInstall(targetDir, platform, _force) {
|
|
|
81
197
|
}
|
|
82
198
|
};
|
|
83
199
|
collectFiles(targetPlatform, platformFolder);
|
|
200
|
+
// Migrate legacy skills from platform-specific dir to .agents/skills/
|
|
201
|
+
const migrationResult = migrateLegacySkills(targetDir, platform);
|
|
202
|
+
for (const skill of migrationResult.migrated) {
|
|
203
|
+
filesCreated.push(`.agents/skills/${skill} (migrated from ${platformFolder}/skills/)`);
|
|
204
|
+
}
|
|
205
|
+
for (const skill of migrationResult.removed) {
|
|
206
|
+
filesSkipped.push(`.agents/skills/${skill} (already existed, removed legacy copy)`);
|
|
207
|
+
}
|
|
208
|
+
// Copy universal skills to .agents/skills/ (AgentSkills.io convention)
|
|
209
|
+
const universalResult = copyUniversalSkills(targetDir);
|
|
210
|
+
for (const skill of universalResult.copied) {
|
|
211
|
+
filesCreated.push(`.agents/skills/${skill}`);
|
|
212
|
+
}
|
|
213
|
+
for (const skill of universalResult.skipped) {
|
|
214
|
+
filesSkipped.push(`.agents/skills/${skill} (already exists, preserved)`);
|
|
215
|
+
}
|
|
216
|
+
// Merge custom skills from skills-custom/ (e.g., wp-wpengine)
|
|
217
|
+
const customResult = mergeCustomSkills(targetDir);
|
|
218
|
+
for (const skill of customResult.merged) {
|
|
219
|
+
filesCreated.push(`.agents/skills/${skill}`);
|
|
220
|
+
}
|
|
221
|
+
for (const skill of customResult.skipped) {
|
|
222
|
+
filesSkipped.push(`.agents/skills/${skill} (already exists, preserved)`);
|
|
223
|
+
}
|
|
84
224
|
// Copy AGENTS.md template
|
|
85
225
|
const targetAgentsTemplate = path.join(targetDir, 'AGENTS.template.md');
|
|
86
226
|
if (fs.existsSync(templatePath)) {
|
|
@@ -140,6 +280,30 @@ function safeUpdateInstall(targetDir, platform, options) {
|
|
|
140
280
|
else {
|
|
141
281
|
filesSkipped.push('AGENTS.md (preserved)');
|
|
142
282
|
}
|
|
283
|
+
// Migrate legacy skills from platform-specific dir to .agents/skills/
|
|
284
|
+
const migrationResult = migrateLegacySkills(targetDir, platform);
|
|
285
|
+
for (const skill of migrationResult.migrated) {
|
|
286
|
+
filesCreated.push(`.agents/skills/${skill} (migrated from ${PLATFORM_FOLDERS[platform]}/skills/)`);
|
|
287
|
+
}
|
|
288
|
+
for (const skill of migrationResult.removed) {
|
|
289
|
+
filesSkipped.push(`.agents/skills/${skill} (already existed, removed legacy copy)`);
|
|
290
|
+
}
|
|
291
|
+
// Copy universal skills to .agents/skills/ (AgentSkills.io convention)
|
|
292
|
+
const universalResult = copyUniversalSkills(targetDir);
|
|
293
|
+
for (const skill of universalResult.copied) {
|
|
294
|
+
filesCreated.push(`.agents/skills/${skill}`);
|
|
295
|
+
}
|
|
296
|
+
for (const skill of universalResult.skipped) {
|
|
297
|
+
filesSkipped.push(`.agents/skills/${skill} (already exists, preserved)`);
|
|
298
|
+
}
|
|
299
|
+
// Merge custom skills from skills-custom/ (e.g., wp-wpengine)
|
|
300
|
+
const customResult = mergeCustomSkills(targetDir);
|
|
301
|
+
for (const skill of customResult.merged) {
|
|
302
|
+
filesCreated.push(`.agents/skills/${skill}`);
|
|
303
|
+
}
|
|
304
|
+
for (const skill of customResult.skipped) {
|
|
305
|
+
filesSkipped.push(`.agents/skills/${skill} (already exists, preserved)`);
|
|
306
|
+
}
|
|
143
307
|
return {
|
|
144
308
|
targetDir,
|
|
145
309
|
platform,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
1
2
|
import path from 'node:path';
|
|
2
3
|
import { fileURLToPath } from 'node:url';
|
|
3
4
|
/**
|
|
4
5
|
* WordPress Agent Kit — Pi Extension
|
|
5
6
|
*
|
|
6
7
|
* Provides Pi Coding Agent with WordPress development tools:
|
|
7
|
-
* -
|
|
8
|
+
* - 18 WordPress agent skills (17 upstream + wp-wpengine custom) at .agents/skills/ (AgentSkills.io convention)
|
|
8
9
|
* - Project triage detection
|
|
9
|
-
* - Skill installation, syncing, and
|
|
10
|
+
* - Skill installation, syncing, upgrade, and orphan cleanup
|
|
10
11
|
*
|
|
11
12
|
* Follows Pi Coding Agent SDK conventions (extensions.md, packages.md, skills.md).
|
|
12
13
|
*/
|
|
@@ -25,17 +26,43 @@ try {
|
|
|
25
26
|
apiModule = (await import('../../src/lib/api.js')) as typeof import('../../dist/lib/api.js');
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
const { installKitApi, syncSkillsApi, runTriageApi, isKitInstalled, loadManifest } =
|
|
29
|
+
const { installKitApi, syncSkillsApi, runTriageApi, cleanSkillsApi, isKitInstalled, loadManifest } =
|
|
30
|
+
apiModule;
|
|
29
31
|
|
|
30
32
|
export default function (pi: ExtensionAPI) {
|
|
31
33
|
// =========================================================================
|
|
32
34
|
// Skills Registration
|
|
33
35
|
// =========================================================================
|
|
36
|
+
// Primary skills (.agents/skills/) are registered via `pi.skills` in package.json.
|
|
37
|
+
// Pi also auto-discovers from .agents/skills/ at the project level (AgentSkills.io convention).
|
|
38
|
+
// The resources_discover handler ONLY supplements skills that aren't already
|
|
39
|
+
// covered by the static manifest — specifically, custom skills from
|
|
40
|
+
// skills-custom/ that haven't been synced into .agents/skills/ yet.
|
|
41
|
+
// This avoids name collisions (Pi keeps first-loaded, warns on duplicates).
|
|
34
42
|
pi.on('resources_discover', async (_event, _ctx) => {
|
|
35
|
-
const
|
|
43
|
+
const canonicalSkillsDir = path.join(PACKAGE_ROOT, '.agents', 'skills');
|
|
44
|
+
const customSkillsDir = path.join(PACKAGE_ROOT, 'skills-custom');
|
|
36
45
|
const promptsDir = path.join(PACKAGE_ROOT, '.github', 'prompts');
|
|
46
|
+
|
|
47
|
+
// Only discover custom skills that aren't already in .agents/skills/
|
|
48
|
+
// (which is registered via pi.skills in package.json).
|
|
49
|
+
const skillPaths: string[] = [];
|
|
50
|
+
if (fs.existsSync(customSkillsDir)) {
|
|
51
|
+
const existingSkills = fs.existsSync(canonicalSkillsDir)
|
|
52
|
+
? new Set(fs.readdirSync(canonicalSkillsDir))
|
|
53
|
+
: new Set<string>();
|
|
54
|
+
for (const entry of fs.readdirSync(customSkillsDir)) {
|
|
55
|
+
if (!existingSkills.has(entry)) {
|
|
56
|
+
const entryPath = path.join(customSkillsDir, entry);
|
|
57
|
+
if (fs.statSync(entryPath).isDirectory()) {
|
|
58
|
+
skillPaths.push(entryPath);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
37
64
|
return {
|
|
38
|
-
skillPaths:
|
|
65
|
+
skillPaths: skillPaths.length > 0 ? skillPaths : undefined,
|
|
39
66
|
promptPaths: [promptsDir],
|
|
40
67
|
};
|
|
41
68
|
});
|
|
@@ -127,7 +154,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
127
154
|
name: 'wp_install_kit',
|
|
128
155
|
label: 'WP Install Kit',
|
|
129
156
|
description:
|
|
130
|
-
'Install WordPress Agent Kit into a project directory. Copies
|
|
157
|
+
'Install WordPress Agent Kit into a project directory. Copies 18 WordPress skills to .agents/skills/ (AgentSkills.io convention), platform-specific agents/instructions/prompts, and an AGENTS.md template. Safe by default — preserves user modifications on re-run.',
|
|
131
158
|
promptSnippet: 'Install WordPress AI agent skills and configuration into a project',
|
|
132
159
|
promptGuidelines: [
|
|
133
160
|
'Use wp_install_kit when setting up a new WordPress project for AI agent development.',
|
|
@@ -243,7 +270,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
243
270
|
name: 'wp_sync_skills',
|
|
244
271
|
label: 'WP Sync Skills',
|
|
245
272
|
description:
|
|
246
|
-
'Sync WordPress agent skills from the upstream WordPress/agent-skills repository. Fetches the latest
|
|
273
|
+
'Sync WordPress agent skills from the upstream WordPress/agent-skills repository. Fetches the latest upstream skill definitions and merges custom skills from skills-custom/ — custom skills survive upstream syncs.',
|
|
247
274
|
promptSnippet: 'Sync latest WordPress agent skills from upstream',
|
|
248
275
|
promptGuidelines: [
|
|
249
276
|
'Use wp_sync_skills to pull the latest WordPress development skills from WordPress/agent-skills.',
|
|
@@ -273,11 +300,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
273
300
|
}
|
|
274
301
|
|
|
275
302
|
const data = result.data as { skillsSynced: number; method: string };
|
|
303
|
+
const customSkillNote = fs.existsSync(path.join(PACKAGE_ROOT, 'skills-custom'))
|
|
304
|
+
? '\n**Custom skills**: Merged from skills-custom/'
|
|
305
|
+
: '';
|
|
276
306
|
return {
|
|
277
307
|
content: [
|
|
278
308
|
{
|
|
279
309
|
type: 'text',
|
|
280
|
-
text: `# Skills Synced\n\n**Synced**: ${data.skillsSynced} skills\n**Method**: ${data.method}`,
|
|
310
|
+
text: `# Skills Synced\n\n**Synced**: ${data.skillsSynced} skills\n**Method**: ${data.method}${customSkillNote}`,
|
|
281
311
|
},
|
|
282
312
|
],
|
|
283
313
|
details: data,
|
|
@@ -315,7 +345,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
315
345
|
const currentVersion = manifest?.version || 'not installed';
|
|
316
346
|
const latestVersion = (() => {
|
|
317
347
|
try {
|
|
318
|
-
const fs = require('node:fs');
|
|
319
348
|
return JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf-8'))
|
|
320
349
|
.version;
|
|
321
350
|
} catch {
|
|
@@ -393,6 +422,114 @@ export default function (pi: ExtensionAPI) {
|
|
|
393
422
|
},
|
|
394
423
|
});
|
|
395
424
|
|
|
425
|
+
// --- wp_clean_skills ---
|
|
426
|
+
pi.registerTool({
|
|
427
|
+
name: 'wp_clean_skills',
|
|
428
|
+
label: 'WP Clean Skills',
|
|
429
|
+
description:
|
|
430
|
+
'Detect and remove orphaned skills from a WordPress Agent Kit installation. Compares installed skills against the canonical set (upstream + custom) and reports or removes skills that are no longer part of the kit. Safe by default — use dryRun first to preview.',
|
|
431
|
+
promptSnippet: 'Clean up orphaned WordPress agent skills',
|
|
432
|
+
promptGuidelines: [
|
|
433
|
+
'Use wp_clean_skills after upgrading to remove skills that are no longer part of the kit.',
|
|
434
|
+
'Always run with dryRun: true first to preview what would be removed.',
|
|
435
|
+
'This only removes skill directories — it does not modify AGENTS.md or other config files.',
|
|
436
|
+
],
|
|
437
|
+
parameters: Type.Object({
|
|
438
|
+
targetDir: Type.Optional(
|
|
439
|
+
Type.String({
|
|
440
|
+
description: 'WordPress project directory (defaults to current working directory)',
|
|
441
|
+
})
|
|
442
|
+
),
|
|
443
|
+
dryRun: Type.Optional(
|
|
444
|
+
Type.Boolean({
|
|
445
|
+
description: 'Preview changes without applying (default: true for safety)',
|
|
446
|
+
})
|
|
447
|
+
),
|
|
448
|
+
remove: Type.Optional(
|
|
449
|
+
Type.Boolean({
|
|
450
|
+
description: 'Actually remove orphaned skills (default: false — report only)',
|
|
451
|
+
})
|
|
452
|
+
),
|
|
453
|
+
}),
|
|
454
|
+
async execute(_callId, params, _signal, _onUpdate, _ctx) {
|
|
455
|
+
const targetDir = params.targetDir || process.cwd();
|
|
456
|
+
const dryRun = params.dryRun ?? true;
|
|
457
|
+
const remove = params.remove ?? false;
|
|
458
|
+
|
|
459
|
+
const result = await cleanSkillsApi({
|
|
460
|
+
targetDir,
|
|
461
|
+
platform: 'pi',
|
|
462
|
+
dryRun,
|
|
463
|
+
remove,
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
if (!result.success) {
|
|
467
|
+
return {
|
|
468
|
+
content: [
|
|
469
|
+
{ type: 'text', text: `Clean failed: ${result.error?.message || 'Unknown error'}` },
|
|
470
|
+
],
|
|
471
|
+
isError: true,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const data = result.data as {
|
|
476
|
+
orphanedSkills: string[];
|
|
477
|
+
removedSkills: string[];
|
|
478
|
+
legacySkillDirs: string[];
|
|
479
|
+
migratedSkills: string[];
|
|
480
|
+
dryRun: boolean;
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
const hasNoIssues = data.orphanedSkills.length === 0 && data.legacySkillDirs.length === 0;
|
|
484
|
+
if (hasNoIssues) {
|
|
485
|
+
return {
|
|
486
|
+
content: [
|
|
487
|
+
{
|
|
488
|
+
type: 'text',
|
|
489
|
+
text: '# All Clean\n\nAll installed skills match the canonical set. No orphaned or legacy skills found.',
|
|
490
|
+
},
|
|
491
|
+
],
|
|
492
|
+
details: data,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const lines = [dryRun ? '# Skills Cleanup Preview (Dry Run)' : '# Skills Cleaned Up', ''];
|
|
497
|
+
|
|
498
|
+
if (data.orphanedSkills.length > 0) {
|
|
499
|
+
lines.push(`**Orphaned skills** (${data.orphanedSkills.length}):`);
|
|
500
|
+
for (const skill of data.orphanedSkills) {
|
|
501
|
+
lines.push(`- ${skill}`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (data.legacySkillDirs.length > 0) {
|
|
506
|
+
lines.push('', `**Legacy skill directories** (${data.legacySkillDirs.length}):`);
|
|
507
|
+
for (const dir of data.legacySkillDirs) {
|
|
508
|
+
lines.push(`- ${dir}`);
|
|
509
|
+
}
|
|
510
|
+
lines.push('These will be migrated to `.agents/skills/` and then removed.');
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (dryRun) {
|
|
514
|
+
lines.push('', 'Run with `remove: true` to clean up.');
|
|
515
|
+
} else {
|
|
516
|
+
if (data.removedSkills.length > 0) {
|
|
517
|
+
lines.push('', `Removed **${data.removedSkills.length}** orphaned skill(s).`);
|
|
518
|
+
}
|
|
519
|
+
if (data.migratedSkills.length > 0) {
|
|
520
|
+
lines.push(
|
|
521
|
+
`Migrated **${data.migratedSkills.length}** skill(s) from legacy directories.`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return {
|
|
527
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
528
|
+
details: data,
|
|
529
|
+
};
|
|
530
|
+
},
|
|
531
|
+
});
|
|
532
|
+
|
|
396
533
|
// =========================================================================
|
|
397
534
|
// Commands
|
|
398
535
|
// =========================================================================
|
|
@@ -431,7 +568,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
431
568
|
`${data.isUpdate ? 'Updated' : 'Installed'} (${data.filesCreated.length} entries)`,
|
|
432
569
|
'info'
|
|
433
570
|
);
|
|
434
|
-
|
|
571
|
+
const pkgVersion = (() => {
|
|
572
|
+
try {
|
|
573
|
+
return JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf-8'))
|
|
574
|
+
.version;
|
|
575
|
+
} catch {
|
|
576
|
+
return 'unknown';
|
|
577
|
+
}
|
|
578
|
+
})();
|
|
579
|
+
ctx.ui.setStatus('wp-agent-kit', `v${pkgVersion} installed`);
|
|
435
580
|
},
|
|
436
581
|
});
|
|
437
582
|
|
|
@@ -449,4 +594,34 @@ export default function (pi: ExtensionAPI) {
|
|
|
449
594
|
ctx.ui.setStatus('wp-agent-kit', `${data.skillsSynced} skills`);
|
|
450
595
|
},
|
|
451
596
|
});
|
|
597
|
+
|
|
598
|
+
pi.registerCommand('wp-clean-skills', {
|
|
599
|
+
description: 'Detect and remove orphaned/legacy skills from WordPress Agent Kit',
|
|
600
|
+
handler: async (args, ctx) => {
|
|
601
|
+
const targetDir = args?.trim() || ctx.cwd;
|
|
602
|
+
ctx.ui.setStatus('wp-clean', 'Checking for orphaned and legacy skills...');
|
|
603
|
+
const result = await cleanSkillsApi({
|
|
604
|
+
targetDir,
|
|
605
|
+
platform: 'pi',
|
|
606
|
+
dryRun: true,
|
|
607
|
+
remove: false,
|
|
608
|
+
});
|
|
609
|
+
if (!result.success) {
|
|
610
|
+
ctx.ui.notify(`Clean failed: ${result.error?.message}`, 'error');
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
const data = result.data as { orphanedSkills: string[]; legacySkillDirs: string[] };
|
|
614
|
+
const total = data.orphanedSkills.length + data.legacySkillDirs.length;
|
|
615
|
+
if (total === 0) {
|
|
616
|
+
ctx.ui.notify('No orphaned or legacy skills found', 'info');
|
|
617
|
+
} else {
|
|
618
|
+
const parts: string[] = [];
|
|
619
|
+
if (data.orphanedSkills.length > 0) parts.push(`${data.orphanedSkills.length} orphaned`);
|
|
620
|
+
if (data.legacySkillDirs.length > 0)
|
|
621
|
+
parts.push(`${data.legacySkillDirs.length} legacy dir(s)`);
|
|
622
|
+
ctx.ui.notify(`Found ${parts.join(', ')}`, 'info');
|
|
623
|
+
}
|
|
624
|
+
ctx.ui.setStatus('wp-clean', total === 0 ? 'clean' : `${total} issues`);
|
|
625
|
+
},
|
|
626
|
+
});
|
|
452
627
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wordpress-agent-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "WordPress-focused AGENTS.md + Agent Skills starter kit for AI coding agents.",
|
|
5
5
|
"license": "GPL-2.0-or-later",
|
|
6
6
|
"author": "Kyle Brodeur <kyle@brodeur.me> (https://brodeur.me)",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"./extensions/wp-agent-kit"
|
|
32
32
|
],
|
|
33
33
|
"skills": [
|
|
34
|
-
"./.
|
|
34
|
+
"./.agents/skills"
|
|
35
35
|
]
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
@@ -44,28 +44,20 @@
|
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@biomejs/biome": "1.9.4",
|
|
47
|
-
"@
|
|
47
|
+
"@earendil-works/pi-coding-agent": "^0.78.1",
|
|
48
48
|
"@types/node": "^22.10.1",
|
|
49
49
|
"@vitest/coverage-v8": "^2.1.8",
|
|
50
|
-
"eslint": "^9.16.0",
|
|
51
50
|
"husky": "^9.1.6",
|
|
52
|
-
"prettier": "^3.4.1",
|
|
53
51
|
"ts-node": "^10.9.2",
|
|
54
52
|
"tsx": "^4.19.2",
|
|
53
|
+
"typebox": "*",
|
|
55
54
|
"typescript": "^5.7.2",
|
|
56
|
-
"
|
|
57
|
-
"vitest": "^2.1.8",
|
|
58
|
-
"@earendil-works/pi-coding-agent": "^0.78.1",
|
|
59
|
-
"typebox": "*"
|
|
55
|
+
"vitest": "^2.1.8"
|
|
60
56
|
},
|
|
61
57
|
"scripts": {
|
|
62
58
|
"dev": "tsx src/cli.ts",
|
|
63
59
|
"build": "tsc",
|
|
64
60
|
"check": "tsc --noEmit",
|
|
65
|
-
"lint": "eslint src/ tests/ --fix && biome check --write .",
|
|
66
|
-
"lint:check": "eslint src/ tests/ && biome check .",
|
|
67
|
-
"format": "prettier --write . && biome format --write .",
|
|
68
|
-
"format:check": "prettier --check . && biome format --check .",
|
|
69
61
|
"test": "vitest",
|
|
70
62
|
"test:run": "vitest run",
|
|
71
63
|
"test:coverage": "vitest run --coverage",
|
|
@@ -73,6 +65,10 @@
|
|
|
73
65
|
"sync:skills": "tsx src/cli.ts sync-skills",
|
|
74
66
|
"setup": "tsx src/cli.ts setup",
|
|
75
67
|
"playground": "tsx src/cli.ts playground",
|
|
76
|
-
"build:bundles": "tsx scripts/build-bundles.ts"
|
|
68
|
+
"build:bundles": "tsx scripts/build-bundles.ts",
|
|
69
|
+
"lint": "biome check --write .",
|
|
70
|
+
"lint:check": "biome check .",
|
|
71
|
+
"format": "biome format --write .",
|
|
72
|
+
"format:check": "biome format --check ."
|
|
77
73
|
}
|
|
78
74
|
}
|