wordpress-agent-kit 0.3.0 → 0.3.2

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.
Files changed (31) hide show
  1. package/.github/skills/blueprint/SKILL.md +418 -0
  2. package/.github/skills/wp-abilities-api/SKILL.md +12 -0
  3. package/.github/skills/wp-abilities-api/references/delegate-helper-pattern.md +241 -0
  4. package/.github/skills/wp-abilities-api/references/domain-vs-projection.md +113 -0
  5. package/.github/skills/wp-abilities-api/references/error-code-vocabulary.md +123 -0
  6. package/.github/skills/wp-abilities-api/references/grouping-heuristic.md +89 -0
  7. package/.github/skills/wp-abilities-api/references/input-schema-gotchas.md +265 -0
  8. package/.github/skills/wp-abilities-api/references/php-registration.md +47 -20
  9. package/.github/skills/wp-abilities-api/references/plugin-family-patterns.md +233 -0
  10. package/.github/skills/wp-abilities-api/references/shared-core-service.md +184 -0
  11. package/.github/skills/wp-abilities-audit/SKILL.md +199 -0
  12. package/.github/skills/wp-abilities-audit/references/audit-schema.md +300 -0
  13. package/.github/skills/wp-abilities-audit/references/capability-gate-tracing.md +197 -0
  14. package/.github/skills/wp-abilities-audit/references/controller-enumeration.md +116 -0
  15. package/.github/skills/wp-abilities-verify/SKILL.md +215 -0
  16. package/.github/skills/wp-abilities-verify/references/annotation-correctness.md +154 -0
  17. package/.github/skills/wp-abilities-verify/references/audit-schema-validation.md +131 -0
  18. package/.github/skills/wp-abilities-verify/references/permission-roundtrip.md +190 -0
  19. package/.github/skills/wp-abilities-verify/references/runtime-harness.md +462 -0
  20. package/.github/skills/wp-abilities-verify/references/schema-lints.md +118 -0
  21. package/.github/skills/wp-abilities-verify/references/static-enumeration.md +126 -0
  22. package/.github/skills/wp-plugin-directory-guidelines/SKILL.md +133 -0
  23. package/.github/skills/wp-plugin-directory-guidelines/references/gpl-compliance.md +217 -0
  24. package/.github/skills/wp-plugin-directory-guidelines/references/guideline-review-checklist.md +592 -0
  25. package/.github/skills/wp-plugin-directory-guidelines/references/naming-rules.md +121 -0
  26. package/.github/skills/wp-project-triage/scripts/detect_wp_project.mjs +22 -4
  27. package/README.md +6 -3
  28. package/dist/lib/api.js +30 -19
  29. package/dist/lib/installer.js +0 -2
  30. package/extensions/wp-agent-kit/index.ts +146 -324
  31. package/package.json +1 -1
@@ -0,0 +1,121 @@
1
+ ## Plugin Naming Rules (Guideline 17 + Plugin Check Namer)
2
+
3
+ Sources: [Plugin Header Requirements](https://developer.wordpress.org/plugins/plugin-basics/header-requirements/#header-fields) · [Detailed Plugin Guidelines §17](https://developer.wordpress.org/plugins/wordpress-org/detailed-plugin-guidelines/) · Plugin Check `Plugin_Header_Fields_Check`, `Trademarks_Check`, and AI Namer prompts.
4
+
5
+ ### Technical Name Requirements
6
+
7
+ | Rule | Details | Error Code |
8
+ |------|---------|------------|
9
+ | Must not use placeholder names | `"Plugin Name"` or `"My Basics Plugin"` are rejected | `plugin_header_invalid_plugin_name` |
10
+ | Minimum 5 alphanumeric characters | Name must contain at least 5 latin letters (a–Z) or digits | `plugin_header_unsupported_plugin_name` (new plugins only) |
11
+ | Name must exist in readme | `=== Plugin Name ===` header required and must be valid | `invalid_plugin_name` / `empty_plugin_name` |
12
+ | Name must match across files | Readme and plugin header name must match (case/entity-decoded) | `mismatched_plugin_name` (warning) |
13
+
14
+ **Slug rules:** lowercase, hyphens only, max 50 characters, derived from display name.
15
+
16
+ ### Naming Quality Rules (AI Namer)
17
+
18
+ **1. No generic names**
19
+ Names must be specific enough to distinguish the plugin from ~60,000 others.
20
+ - Rejected: "Shipping", "Ecommerce Tracker", "SEO Plugin"
21
+ - Accepted: "ShipGlex Shipping", "Shipping Tracker for UPS"
22
+ - Exception: invented/original terms are allowed if placed at the **beginning** of the name
23
+
24
+ **2. Name must relate to plugin function**
25
+ The display name must correlate with what the plugin actually does. Exception: original invented terms.
26
+
27
+ **3. No keyword stuffing**
28
+ Unnaturally repeating keywords in the name for SEO purposes is not allowed.
29
+
30
+ **4. No names too similar to existing plugins**
31
+ Checked against the WordPress.org Plugin Directory. If similar, suggest a distinctive term (author name, brand, or crafted term) at the beginning.
32
+
33
+ **5. Trademark/project name usage rules**
34
+ Trademarks and project names are allowed **only** after connectors like `for`, `with`, `using`, or `and`:
35
+ - ✅ `"My Plugin for WooCommerce"` — trademark after "for", no affiliation implied
36
+ - ✅ `"Pricing Rates for WooCommerce"` — OK
37
+ - ❌ `"WooCommerce Pricing Rates"` — starts with trademark, implies affiliation
38
+ - ❌ `"Nicedev Paypal for WooCommerce"` — PayPal is not after a no-affiliation structure; correct form: `"Nicedev Payment Gateway with PayPal for WooCommerce"`
39
+ - ❌ `"PricingPress"` — portmanteau using `-Press` (WordPress trademark)
40
+ - Check for portmanteaus: names blending a trademark (e.g., `-Press`, `Woo-`) are not allowed
41
+
42
+ **6. Banned and discouraged terms**
43
+
44
+ Banned/discouraged terms cannot appear **anywhere** in the name — not even after `for`/`with`.
45
+
46
+ #### Banned Terms (hard block)
47
+
48
+ | Term | Reason |
49
+ |------|--------|
50
+ | Facebook, FB, fbook, Whatsapp, WA, Instagram, Insta, Gram, INS, Threads, Oculus | Meta legal request: no use in name, slug, or banners |
51
+ | WordPress, wordpess, wpress | WordPress trademark; redundant in the WP.org directory |
52
+ | WP (as standalone/redundant, e.g., "for WP") | Same as WordPress — redundant in context |
53
+ | Trustpilot | Direct request from trademark holder |
54
+ | Binance Pay | Direct request from trademark holder |
55
+
56
+ #### Discouraged Terms (must be removed)
57
+
58
+ | Term | Reason |
59
+ |------|--------|
60
+ | plugin (when redundant, e.g., "SEO Plugin") | Redundant; forbidden as first word |
61
+ | best, #1, First, Perfect, The most | Superlatives / unverifiable comparative claims |
62
+ | free (when redundant, e.g., "(free)") | All directory plugins are free — redundant |
63
+ | WP, W P (at beginning or end, referring to WordPress) | WordPress abbreviation — redundant |
64
+ | Gutenberg, gberg, guten, berg | Creates confusion; block editor is the current name |
65
+
66
+ ### Trademark Slug List (static check — `Trademarks_Check`)
67
+
68
+ The following slugs are statically blocked. Terms ending in `-` cannot **begin** a slug; terms without `-` cannot appear **anywhere** in the slug. `woocommerce` (no dash) is allowed only as `for-woocommerce`, `with-woocommerce`, `using-woocommerce`, or `and-woocommerce`.
69
+
70
+ ```
71
+ adobe-, adsense-, advanced-custom-fields-, adwords-, akismet-,
72
+ all-in-one-wp-migration, amazon-, android-, apple-, applenews-, applepay-,
73
+ aws-, azon-, bbpress-, bing-, booking-com, bootstrap-, buddypress-,
74
+ chatgpt-, chat-gpt-, cloudflare-, contact-form-7-, cpanel-, disqus-, divi-,
75
+ dropbox-, easy-digital-downloads-, elementor-, envato-,
76
+ fbook, facebook, fb-, fb-messenger, fedex-, feedburner, firefox-,
77
+ fontawesome-, font-awesome-, ganalytics-, gberg, github-, givewp-, google-,
78
+ googlebot-, googles-, gravity-form-, gravity-forms-, gravityforms-, gtmetrix-,
79
+ gutenberg, guten-, hubspot-, ig-, insta-, instagram, internet-explorer-,
80
+ ios-, jetpack-, macintosh-, macos-, mailchimp-, microsoft-,
81
+ ninja-forms-, oculus, onlyfans-, only-fans-, opera-, paddle-, paypal-,
82
+ pinterest-, plugin, skype-, stripe-, tiktok-, tik-tok-, trustpilot,
83
+ twitch-, twitter-, tweet, ups-, usps-, vvhatsapp, vvcommerce, vva-, vvoo,
84
+ wa-, webpush-vn, wh4tsapps, whatsapp, whats-app, watson, windows-,
85
+ wocommerce, woocom-, woocommerce, woocomerce, woo-commerce, woo-, wo-,
86
+ wordpress, wordpess, wpress, wp, wc, wp-mail-smtp-, yandex-, yahoo-,
87
+ yoast, youtube-, you-tube-
88
+ ```
89
+
90
+ **Portmanteaus also blocked:** any slug starting with `woo` (case-insensitive) — e.g., `woopress`, `wooland`.
91
+
92
+ ### Naming Examples
93
+
94
+ | Plugin Name | Verdict | Reason |
95
+ |-------------|---------|--------|
96
+ | `Shipping` | ❌ | Too generic |
97
+ | `Ecommerce Tracker` | ❌ | Too generic, no context |
98
+ | `Shipping Tracker for UPS` | ✅ | Descriptive + context |
99
+ | `ShipGlex Shipping` | ✅ | Invented term at beginning |
100
+ | `WooCommerce Pricing Rates` | ❌ | Starts with trademark |
101
+ | `Pricing Rates for WooCommerce` | ✅ | Trademark after "for" |
102
+ | `PricingPress` | ❌ | `-Press` portmanteau |
103
+ | `PRT Text editor for WP` | ❌ | WP is banned/redundant; correct: `PRT Text editor` |
104
+ | `Nicedev Paypal for WooCommerce` | ❌ | PayPal not after no-affiliation structure |
105
+ | `Nicedev Payment Gateway with PayPal for WooCommerce` | ✅ | Correct structure |
106
+ | `Best SEO Plugin for WordPress` | ❌ | Superlative + banned terms |
107
+ | `My Free Slider` | ❌ | "free" is redundant/discouraged |
108
+
109
+ ### Naming Review Checklist (pre-submission)
110
+
111
+ - [ ] Name is not a placeholder (`Plugin Name`, `My Basics Plugin`)
112
+ - [ ] Name has at least 5 alphanumeric characters
113
+ - [ ] Name matches between plugin header and readme
114
+ - [ ] Name is specific — not too generic for 60,000+ plugins
115
+ - [ ] Name relates to what the plugin actually does
116
+ - [ ] No keyword stuffing
117
+ - [ ] No banned terms anywhere (Meta brands, WordPress/WP redundant, Trustpilot, Binance Pay)
118
+ - [ ] No discouraged terms (Plugin, Best/#1, Free, Gutenberg, standalone WP)
119
+ - [ ] Trademarks/project names only appear after `for`/`with`/`using`/`and`
120
+ - [ ] No portmanteaus using WordPress or WooCommerce trademarks
121
+ - [ ] Slug is lowercase, hyphens only, max 50 chars, no blocked terms
@@ -124,7 +124,8 @@ function findFilesRecursive(repoRoot, predicate, { maxFiles = 6000, maxDepth = 8
124
124
  function detectPluginHeaderFromPhpFile(filePath) {
125
125
  const contents = readFileSafe(filePath, 128 * 1024);
126
126
  if (!contents) return null;
127
- const headerMatch = contents.match(/^\s*Plugin Name:\s*(.+)\s*$/im);
127
+ // Allow leading whitespace and asterisks common in block comments
128
+ const headerMatch = contents.match(/^[ \\t*]*Plugin Name:\s*(.+)\s*$/im);
128
129
  if (!headerMatch) return null;
129
130
  return headerMatch[1].trim();
130
131
  }
@@ -132,7 +133,8 @@ function detectPluginHeaderFromPhpFile(filePath) {
132
133
  function detectThemeHeaderFromStyleCss(filePath) {
133
134
  const contents = readFileSafe(filePath, 128 * 1024);
134
135
  if (!contents) return null;
135
- const headerMatch = contents.match(/^\s*Theme Name:\s*(.+)\s*$/im);
136
+ // Allow leading whitespace and asterisks common in block comments
137
+ const headerMatch = contents.match(/^[ \\t*]*Theme Name:\s*(.+)\s*$/im);
136
138
  if (!headerMatch) return null;
137
139
  return headerMatch[1].trim();
138
140
  }
@@ -397,6 +399,13 @@ function main() {
397
399
  maxDepth: 8,
398
400
  });
399
401
 
402
+ const restApiScan = scanForTokens(repoRoot, {
403
+ tokens: ["register_rest_route", "register_rest_field", "WP_REST_Controller"],
404
+ exts: [".php"],
405
+ maxFiles: 2500,
406
+ maxDepth: 8,
407
+ });
408
+
400
409
  const wpCliConfigBasenames = new Set([
401
410
  "wp-cli.yml",
402
411
  "wp-cli.yaml",
@@ -439,6 +448,7 @@ function main() {
439
448
 
440
449
  const usesInteractivityApi = pkgHasInteractivity || Object.keys(interactivityScan.matches).length > 0;
441
450
  const usesAbilitiesApi = pkgHasAbilities || Object.keys(abilitiesScan.matches).length > 0;
451
+ const usesRestApi = Object.keys(restApiScan.matches).length > 0;
442
452
  const usesInnerBlocks = Object.keys(innerBlocksScan.matches).length > 0;
443
453
  const usesWpCli = composerHasWpCli || wpCliConfigFiles.length > 0 || Object.keys(wpCliTokenScan.matches).length > 0;
444
454
 
@@ -476,6 +486,10 @@ function main() {
476
486
  );
477
487
 
478
488
  const hasPhpUnit = phpunitXml.length > 0 || Boolean(composerJson?.requireDev?.phpunit || composerJson?.["require-dev"]?.phpunit);
489
+
490
+ const hasPhpStan = existsFile(path.join(repoRoot, "phpstan.neon")) ||
491
+ existsFile(path.join(repoRoot, "phpstan.neon.dist")) ||
492
+ Boolean(composerJson?.requireDev?.["phpstan/phpstan"] || composerJson?.["require-dev"]?.["phpstan/phpstan"]);
479
493
 
480
494
  const signals = {
481
495
  paths: {
@@ -496,8 +510,7 @@ function main() {
496
510
  isBlockPlugin,
497
511
  isBlockTheme,
498
512
  usesInteractivityApi,
499
- usesAbilitiesApi,
500
- usesInnerBlocks,
513
+ usesAbilitiesApi, usesRestApi, usesInnerBlocks,
501
514
  usesWpCli,
502
515
  performanceHints: {
503
516
  wpConfig: config.source,
@@ -523,6 +536,10 @@ function main() {
523
536
  matches: abilitiesScan.matches,
524
537
  scanTruncated: abilitiesScan.truncated,
525
538
  },
539
+ restApiHints: {
540
+ matches: restApiScan.matches,
541
+ scanTruncated: restApiScan.truncated,
542
+ },
526
543
  innerBlocksHints: {
527
544
  matches: innerBlocksScan.matches,
528
545
  scanTruncated: innerBlocksScan.truncated,
@@ -552,6 +569,7 @@ function main() {
552
569
  php: {
553
570
  hasComposerJson: existsFile(path.join(repoRoot, "composer.json")),
554
571
  hasVendorDir: existsDir(path.join(repoRoot, "vendor")),
572
+ hasPhpStan,
555
573
  phpunitXml,
556
574
  },
557
575
  node: {
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-blue.svg?style=flat-square)](LICENSE)
4
4
  [![TypeScript](https://img.shields.io/badge/Written%20in-TypeScript-3178C6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
5
- [![Version](https://img.shields.io/badge/version-0.3.0-blue?style=flat-square)](package.json)
5
+ [![Version](https://img.shields.io/badge/version-0.3.2-blue?style=flat-square)](package.json)
6
6
  [![Node](https://img.shields.io/badge/node-%3E%3D20.18-green?style=flat-square)](package.json)
7
7
  [![CI](https://img.shields.io/badge/CI-passing-brightgreen?style=flat-square)](.github/workflows/ci.yml)
8
8
 
@@ -40,7 +40,7 @@ my-plugin/
40
40
  │ ├── instructions/
41
41
  │ │ └── wordpress-workflow.instructions.md
42
42
  │ ├── prompts/
43
- │ └── skills/ # 13 WordPress skills
43
+ │ └── skills/ # 17 WordPress skills
44
44
  │ ├── wp-project-triage/ # Project detection
45
45
  │ ├── wp-plugin-development/ # Plugin architecture
46
46
  │ ├── wp-block-development/ # Gutenberg blocks
@@ -231,7 +231,6 @@ All 13 skills follow the [AgentSkills.io](https://agentskills.io) specification:
231
231
 
232
232
  | Skill | When to Use |
233
233
  |-------|------------|
234
- | `wordpress-router` | Classify a WordPress repo and route to the right workflow |
235
234
  | `wp-project-triage` | Run deterministic project detection (type, tooling, versions) |
236
235
  | `wp-plugin-development` | Develop WordPress plugins (hooks, settings, security, release) |
237
236
  | `wp-block-development` | Develop Gutenberg blocks (block.json, attributes, rendering) |
@@ -239,11 +238,15 @@ All 13 skills follow the [AgentSkills.io](https://agentskills.io) specification:
239
238
  | `wp-rest-api` | Build, extend, or debug REST API endpoints/routes |
240
239
  | `wp-interactivity-api` | Build Interactive blocks with data-wp-* directives |
241
240
  | `wp-abilities-api` | Register and consume WordPress Abilities API |
241
+ | `wp-abilities-audit` | Audit a plugin's REST surface for Abilities API opportunities |
242
+ | `wp-abilities-verify` | Verify registered Abilities match their annotations |
242
243
  | `wp-performance` | Profile and optimize WordPress performance |
243
244
  | `wp-phpstan` | Configure and run PHPStan static analysis |
244
245
  | `wp-wpcli-and-ops` | WP-CLI commands, automation, multisite operations |
245
246
  | `wp-playground` | Test in disposable WordPress Playground instances |
247
+ | `blueprint` | Write and edit WordPress Playground blueprint JSON |
246
248
  | `wpds` | Build UIs with the WordPress Design System |
249
+ | `wp-plugin-directory-guidelines` | GPL compliance, naming, slug rules for WP.org submission |
247
250
 
248
251
  ---
249
252
 
package/dist/lib/api.js CHANGED
@@ -25,7 +25,7 @@ export async function installKitApi(options) {
25
25
  installKit(targetDir, platform, { force, safe, backup });
26
26
  return { success: true };
27
27
  });
28
- const filesCreated = getInstalledFiles(targetDir, platform);
28
+ const filesCreated = getInstalledSummary(targetDir, platform);
29
29
  const durationMs = Date.now() - startTime;
30
30
  return formatter.success({
31
31
  targetDir,
@@ -473,38 +473,49 @@ function getPlatformFolder(platform) {
473
473
  return folders[platform];
474
474
  }
475
475
  /**
476
- * Get list of files that would be/are installed.
476
+ * Get summary of installed files grouped by directory.
477
477
  */
478
- function getInstalledFiles(targetDir, platform) {
478
+ function getInstalledSummary(targetDir, platform) {
479
479
  const platformFolder = getPlatformFolder(platform);
480
- const files = [];
480
+ const summary = [];
481
481
  const targetPlatform = path.join(targetDir, platformFolder);
482
482
  if (fs.existsSync(targetPlatform)) {
483
- function walk(dir, prefix = '') {
484
- const entries = fs.readdirSync(dir);
485
- for (const entry of entries) {
486
- const fullPath = path.join(dir, entry);
487
- const relPath = path.join(prefix, entry);
488
- const stat = fs.statSync(fullPath);
489
- if (stat.isDirectory()) {
490
- walk(fullPath, relPath);
491
- }
492
- else {
493
- files.push(path.join(platformFolder, relPath));
483
+ // Walk top-level entries for clean summary
484
+ const topEntries = fs.readdirSync(targetPlatform);
485
+ for (const entry of topEntries) {
486
+ const fullPath = path.join(targetPlatform, entry);
487
+ const stat = fs.statSync(fullPath);
488
+ if (stat.isDirectory()) {
489
+ let totalFiles = 0;
490
+ let totalDirs = 0;
491
+ function countAll(dir) {
492
+ const items = fs.readdirSync(dir);
493
+ for (const item of items) {
494
+ const itemPath = path.join(dir, item);
495
+ const s = fs.statSync(itemPath);
496
+ if (s.isDirectory()) {
497
+ totalDirs++;
498
+ countAll(itemPath);
499
+ }
500
+ else {
501
+ totalFiles++;
502
+ }
503
+ }
494
504
  }
505
+ countAll(fullPath);
506
+ summary.push(`${platformFolder}/${entry}/ (${totalDirs + 1} dirs, ${totalFiles} files)`);
495
507
  }
496
508
  }
497
- walk(targetPlatform);
498
509
  }
499
510
  const agentsPath = path.join(targetDir, 'AGENTS.md');
500
511
  if (fs.existsSync(agentsPath)) {
501
- files.push('AGENTS.md');
512
+ summary.push('AGENTS.md');
502
513
  }
503
514
  const agentsTemplatePath = path.join(targetDir, 'AGENTS.template.md');
504
515
  if (fs.existsSync(agentsTemplatePath)) {
505
- files.push('AGENTS.template.md');
516
+ summary.push('AGENTS.template.md');
506
517
  }
507
- return files;
518
+ return summary;
508
519
  }
509
520
  export { ExitCode } from '../utils/exit-codes.js';
510
521
  export { OutputFormatter, createFormatter, parseOutputFormat } from '../utils/output.js';
@@ -45,7 +45,6 @@ function isKitAlreadyInstalled(targetDir, platform) {
45
45
  */
46
46
  function fullInstall(targetDir, platform, _force) {
47
47
  const platformFolder = PLATFORM_FOLDERS[platform];
48
- console.log(`Installing WordPress Agent Kit (${platform}) into: ${targetDir}`);
49
48
  if (!fs.existsSync(targetDir)) {
50
49
  fs.mkdirSync(targetDir, { recursive: true });
51
50
  }
@@ -117,7 +116,6 @@ function fullInstall(targetDir, platform, _force) {
117
116
  * Safe update install using the updater module.
118
117
  */
119
118
  function safeUpdateInstall(targetDir, platform, options) {
120
- console.log(`Updating WordPress Agent Kit (${platform}) in: ${targetDir}`);
121
119
  const updateOptions = {
122
120
  targetDir,
123
121
  platform,