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,118 @@
1
+ # Schema Lints
2
+
3
+ Static lints against an ability's `input_schema`. Schema hygiene is
4
+ about *agent legibility*: orchestrating agents read the schema to
5
+ figure out how to call the ability. A schema that's hard to parse,
6
+ ambiguous, or misleading wastes turns even when the ability itself
7
+ works.
8
+
9
+ These lints are six small principles. Apply them by reading the
10
+ schema, not by mechanically grepping — most plugins use enough
11
+ formatting variety that grep recipes drift.
12
+
13
+ ## Lint 1 — `additionalProperties: false` for object schemas
14
+
15
+ For top-level `'type' => 'object'` schemas, declare
16
+ `'additionalProperties' => false` unless you deliberately accept
17
+ extras. Without this, an agent passing a typo (`par_page` instead of
18
+ `per_page`) gets accepted silently and falls through to the backing,
19
+ which ignores the unknown key.
20
+
21
+ - `additionalProperties: false` declared → OK.
22
+ - `additionalProperties: true` declared → WARN, unless the schema is
23
+ for genuinely free-form metadata (payment custom fields, form
24
+ free-text); document the reason inline.
25
+ - Not declared on an object schema → WARN.
26
+ - Non-object root (string with enum, integer, etc.) → N/A. The lint
27
+ applies only to objects.
28
+
29
+ ## Lint 2 — every required field has a non-empty description
30
+
31
+ For each entry in `required`, the matching `properties` entry must
32
+ declare a non-empty `description`. Required fields are where agents
33
+ most need guidance; an opaque required key forces the agent to guess
34
+ from the field name alone. Empty / missing → FAIL.
35
+
36
+ Optional-field descriptions are nice-to-have — absence is WARN.
37
+
38
+ ## Lint 3 — enums are non-empty
39
+
40
+ `'enum' => []` accepts no values, rejecting every input. Almost always
41
+ a bug. → FAIL.
42
+
43
+ A single-value enum (`'enum' => [ 'pending' ]`) is legal but unusual;
44
+ WARN and prompt for review — often a copy-paste that lost the other
45
+ values.
46
+
47
+ ## Lint 4 — no `$ref`
48
+
49
+ Agents read the schema via REST introspection. A `$ref` forces the
50
+ agent to follow a reference to see the field shape — wastes a turn and
51
+ often breaks because the referenced schema isn't in the same document.
52
+ Inline the shape instead.
53
+
54
+ Any `'$ref'` in the schema → FAIL.
55
+
56
+ ## Lint 5 — defaults are statically constant
57
+
58
+ Each `'default'` value must evaluate to the same shape on every call:
59
+
60
+ - Scalar literals — `true`, `false`, integer, float, quoted string,
61
+ `null` → OK.
62
+ - Empty or all-literal arrays — `[]`, `array()`, `[ 'a', 'b' ]` → OK.
63
+ - Literal cast to an empty object — `(object) array()`, `(object) []`
64
+ → OK. This is the recommended top-level default for zero-arg-allowed
65
+ abilities; see
66
+ `../../wp-abilities-api/references/input-schema-gotchas.md` §4.
67
+ - `new stdClass()` with no arguments → OK.
68
+ - A function call (`gmdate('c')`, `wp_generate_uuid4()`, `time()`),
69
+ variable reference, or other computed expression → FAIL.
70
+
71
+ The principle: defaults that vary per call are both non-deterministic
72
+ and surprising to agents that expect defaults to be static.
73
+
74
+ ## Lint 6 — `reference_ability: true` implies no required inputs
75
+
76
+ If an audit doc is provided and an ability has
77
+ `reference_ability: true`, its `input_schema.required` array must be
78
+ empty or absent. The reference ability is the smallest, safest
79
+ bootstrap call an implementer lands first; it must work with
80
+ `execute([])`. Required inputs on the reference ability → FAIL.
81
+
82
+ (No audit provided → this lint is skipped — no reference ability is
83
+ declared.)
84
+
85
+ ## Cross-reference: gotchas 1-3 (callback hardening) and gotcha 4 (structural default)
86
+
87
+ Static lints catch shape; the four runtime gotchas in
88
+ `../../wp-abilities-api/references/input-schema-gotchas.md` split into
89
+ two kinds.
90
+
91
+ Gotchas 1-3 need defensive code in the execute callback —
92
+ `array_key_exists` instead of `isset`-only for property defaults,
93
+ pagination key translation, ID validation that accepts `"0"`. These
94
+ are runtime behaviors the callback itself must handle; static schema
95
+ lints can't enforce them.
96
+
97
+ Gotcha 4 — the direct vs indirect invocation strictness — is what
98
+ motivates the `(object) array()` top-level default that Lint 5
99
+ explicitly accepts. This one IS structural and Lint 5 carries the
100
+ enforcement.
101
+
102
+ ## Output format
103
+
104
+ ```markdown
105
+ ## Schema lints
106
+
107
+ | Ability | Lint | Result | Detail |
108
+ |---|---|---|---|
109
+ | <ability> | additionalProperties (object schemas) | WARN | not declared on object schema |
110
+ | <ability> | required-field descriptions | OK | 3/3 required fields documented |
111
+ | <ability> | enum non-empty | OK | no enums |
112
+ | <ability> | no $ref | OK | inline |
113
+ | <ability> | static defaults | FAIL | `created_at` uses `gmdate('c')` |
114
+ | <ability> | reference_ability implies no required | N/A | not reference ability |
115
+ ```
116
+
117
+ A FAIL on any lint flips that ability to FAIL in the run summary.
118
+ WARNs surface but don't block.
@@ -0,0 +1,126 @@
1
+ # Static Enumeration
2
+
3
+ Enumerate a plugin's abilities from source, with no running
4
+ environment. Static enumeration is necessarily best-effort — PHP's
5
+ dynamism (variable indirection, runtime-conditional registration)
6
+ means a complete inventory only comes from a live `wp_get_abilities()`
7
+ call. When static and runtime inventories diverge, trust runtime;
8
+ static drives the diff so the reviewer knows where to look.
9
+
10
+ ## Typical registration shape
11
+
12
+ ```php
13
+ wp_register_ability(
14
+ '<plugin-slug>/<ability-name>',
15
+ array(
16
+ 'label' => __( '...', '<text-domain>' ),
17
+ 'description' => __( '...', '<text-domain>' ),
18
+ 'category' => '<category-slug>',
19
+ 'input_schema' => array( /* ... */ ),
20
+ 'execute_callback' => array( self::class, 'execute_my_ability' ),
21
+ 'permission_callback' => array( self::class, 'check_permission' ),
22
+ 'meta' => array(
23
+ 'annotations' => array(
24
+ 'readonly' => true,
25
+ 'destructive' => false,
26
+ 'idempotent' => true,
27
+ ),
28
+ 'show_in_rest' => true,
29
+ ),
30
+ )
31
+ );
32
+ ```
33
+
34
+ ## Step 1 — find every registration call
35
+
36
+ ```bash
37
+ grep -rn --include='*.php' 'wp_register_ability\s*(' <plugin-root>/
38
+ ```
39
+
40
+ Zero hits → return a clear "no abilities registered" report per
41
+ SKILL.md "Failure modes." Don't fabricate an empty inventory.
42
+
43
+ ## Step 2 — extract each ability name
44
+
45
+ The first argument is the ability name — usually a literal string.
46
+ Real-world formatting splits the call across lines (the example
47
+ above is itself multi-line), so single-line regexes miss common
48
+ cases. Use a multi-line tool:
49
+
50
+ ```bash
51
+ # ripgrep with --multiline + PCRE2 captures the name regardless of line break:
52
+ rg --multiline --pcre2 --type=php -n \
53
+ "wp_register_ability\s*\(\s*['\"]([^'\"]+)['\"]" <plugin-root>/
54
+
55
+ # Fallbacks: pcregrep -M, perl -0777.
56
+ ```
57
+
58
+ If the first argument is a variable or constant
59
+ (`wp_register_ability( $name, ... )`,
60
+ `wp_register_ability( MyPlugin\NAME, ... )`), trace it: a recent
61
+ assignment in the same function or a class constant usually resolves;
62
+ a name computed in a loop won't, in which case flag the limitation and
63
+ recommend runtime mode for authoritative enumeration.
64
+
65
+ ## Step 3 — extract the annotation block
66
+
67
+ Annotations live at `meta.annotations.{readonly,destructive,idempotent}`.
68
+ For each registration, read the array literal forward until the
69
+ matching close. Common shapes:
70
+
71
+ - Multi-line literal (most common).
72
+ - Short-form one-liner.
73
+ - Helper method (`'annotations' => self::annotations_for_read()`) —
74
+ resolve the helper if it returns a literal; otherwise mark
75
+ `<unresolved>` and run the adversarial check against the callback
76
+ alone.
77
+ - Class constant (`'annotations' => self::READONLY_ANNOTATIONS`) —
78
+ resolve the constant.
79
+
80
+ Record `ability_name → declared_annotations`.
81
+
82
+ ## Step 4 — follow `execute_callback` to its body
83
+
84
+ `execute_callback` is one of:
85
+
86
+ ```php
87
+ 'execute_callback' => array( self::class, 'execute_get_things' ),
88
+ 'execute_callback' => array( My_Class::class, 'execute_get_things' ),
89
+ 'execute_callback' => array( $this, 'execute_get_things' ),
90
+ 'execute_callback' => 'my_plugin_execute_get_things', // top-level function
91
+ 'execute_callback' => function ( $input ) { /* ... */ }, // closure
92
+ ```
93
+
94
+ Resolve the reference to its source location: file + start line + end
95
+ line. The annotation-correctness, schema-lint, and permission checks
96
+ all operate on that byte range.
97
+
98
+ ## Limits of static enumeration
99
+
100
+ Cases where the inventory is incomplete or ambiguous:
101
+
102
+ - Variable-indirected names (`foreach` over a slug list).
103
+ - Variable-indirected annotations (built from config).
104
+ - Conditional registration (`if ( feature_enabled() )`).
105
+ - Variable-indirected callbacks (`array( $this, $callback_name )`).
106
+
107
+ Record each in the report's "Static enumeration limitations" section
108
+ and recommend a runtime-mode rerun for the authoritative inventory.
109
+
110
+ ## Output format
111
+
112
+ ```markdown
113
+ ## Static inventory
114
+
115
+ Found <N> ability registrations across <M> files:
116
+
117
+ | Ability | Source file | Registration line | Callback file | Callback lines |
118
+ |---|---|---|---|---|
119
+ | myplugin/get-foo | src/Abilities.php | 42 | src/Abilities.php | 102-134 |
120
+ | myplugin/submit-bar | src/Abilities.php | 68 | src/Services/Bar.php | 58-91 |
121
+
122
+ ### Limitations
123
+
124
+ - <ability>: annotations built dynamically in `annotations_for_read()`;
125
+ recommend runtime mode for annotation cross-check.
126
+ ```
@@ -0,0 +1,133 @@
1
+ ---
2
+ name: wp-plugin-directory-guidelines
3
+ description: "Use when reviewing WordPress plugins for GPL compliance, checking license headers or compatibility, evaluating upsell/freemium/trialware patterns, validating plugin naming or trademark rules, checking plugin slugs, understanding why a plugin was rejected from WordPress.org, or answering any question about the 18 WordPress.org Plugin Directory guidelines — even if the user doesn't mention 'guidelines' explicitly."
4
+ license: GPL-2.0-or-later
5
+ compatibility: "Targets WordPress 6.9+ (PHP 7.2.24+)."
6
+ ---
7
+
8
+ ## Overview
9
+
10
+ Authoritative reference for the 18 WordPress.org Plugin Directory guidelines. Covers GPL licensing, plugin naming/trademark rules, trialware restrictions, and all other submission requirements.
11
+
12
+ ## When to use
13
+
14
+ Use this skill when you need to:
15
+ - Review a WordPress plugin for compliance with the WordPress.org Plugin Directory guidelines
16
+ - Check GPL license compatibility for a plugin or its bundled libraries
17
+ - Verify license headers in plugin files
18
+ - Identify common guideline violations before submission
19
+ - Answer questions about what is or is not allowed on WordPress.org
20
+ - Evaluate premium/upsell flows, license checks, or freemium positioning
21
+ - Review "teaser" or "preview" UI for trialware violations
22
+
23
+ ## Inputs required
24
+
25
+ - Plugin source code (or specific files to review)
26
+ - Optional: plugin readme and plugin header metadata for naming and license checks
27
+
28
+ ## Procedure
29
+
30
+ 1. Check the plugin's license header against the **Valid License Headers** section below.
31
+ 2. Walk through the **18 Guidelines** checklist, paying special attention to Guidelines 1, 4, 5, 7, 8, and 17.
32
+ 3. Confirm trialware/freemium compliance using the checklist in [guideline-review-checklist.md](references/guideline-review-checklist.md) (Guideline 5 section).
33
+ 4. For bundled third-party code, verify license compatibility against **GPL-Compatible Licenses (Quick)** below.
34
+ 5. Flag matches from **Common GPL Violations (Quick)** below.
35
+ 6. For edge cases, consult the detailed references and the [GNU GPL FAQ](https://www.gnu.org/licenses/gpl-faq.html).
36
+
37
+ ## 18-Guideline Review Checklist
38
+
39
+ Use the detailed, per-guideline checklist in [guideline-review-checklist.md](references/guideline-review-checklist.md). Load this reference file only when a full guideline audit is requested.
40
+
41
+ ## GPL Compliance (Guideline 1 in Detail)
42
+
43
+ Use [gpl-compliance.md](references/gpl-compliance.md) for full license tables, compatibility nuances, and examples. Keep this inline section as a quick decision aid.
44
+
45
+ ### Verification (Licensing)
46
+
47
+ - Every licensing-related issue must cite **Guideline 1** and include the file path and exact license string.
48
+ - Confirm compatibility claims against **GPL-Compatible Licenses (Quick)** and escalate ambiguous licenses.
49
+
50
+ ### Failure modes (Licensing)
51
+
52
+ - If a license is not clearly GPL-compatible, do not guess. Check the [GNU license list](https://www.gnu.org/licenses/license-list.html).
53
+ - For dual-license packages, verify both licenses and redistribution terms.
54
+
55
+ ### Quick Reference: WordPress GPL Requirements
56
+
57
+ - WordPress is **GPLv2 or later**.
58
+ - Plugins distributed on WordPress.org must be 100% GPL-compatible (code and assets).
59
+ - Include a valid `License:` header and `License URI:` in the main plugin file.
60
+ - Do not add restrictions that conflict with GPL freedoms.
61
+
62
+ ### Valid License Headers
63
+
64
+ ## GPL Versions Summary
65
+
66
+ | Version | Year | Key Addition |
67
+ |---------|------|--------------|
68
+ | GPLv1 | 1989 | Base copyleft: share-alike for modifications |
69
+ | GPLv2 | 1991 | "Liberty or death" clause (Section 7), clearer distribution terms |
70
+ | GPLv3 | 2007 | Anti-tivoization, explicit patent grants, compatibility provisions |
71
+
72
+ WordPress uses **GPLv2 or later**, meaning plugins can use GPLv2, GPLv3, or "GPLv2 or later".
73
+
74
+ For full license texts, see:
75
+ - [GNU General Public License v1](https://www.gnu.org/licenses/gpl-1.0.html)
76
+ - [GNU General Public License v2](https://www.gnu.org/licenses/gpl-2.0.html)
77
+ - [GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.html)
78
+
79
+ ## License Compliance Checklist
80
+
81
+ When reviewing a plugin, verify:
82
+
83
+ - [ ] Main plugin file has a valid `License:` header (e.g., `GPL-2.0-or-later`, `GPL-2.0+`, `GPLv2 or later`)
84
+ - [ ] Main plugin file has a `License URI:` header pointing to the GPL text
85
+ - [ ] If bundled libraries exist, each has a GPL-compatible license
86
+ - [ ] No "split licensing" (e.g., code GPL but premium features proprietary)
87
+ - [ ] No additional restrictions beyond what GPL allows
88
+ - [ ] No clauses restricting commercial use, modification, or redistribution
89
+ - [ ] No obfuscated code (violates the spirit of source code availability)
90
+
91
+ ## Valid License Headers for WordPress Plugins
92
+
93
+ ```
94
+ License: GPL-2.0-or-later
95
+ License URI: https://www.gnu.org/licenses/gpl-2.0.html
96
+ ```
97
+
98
+ ```text
99
+ License: GPL-3.0-or-later
100
+ License URI: https://www.gnu.org/licenses/gpl-3.0.html
101
+ ```
102
+
103
+ ```text
104
+ License: GPLv2 or later
105
+ License URI: https://www.gnu.org/licenses/gpl-2.0.html
106
+ ```
107
+
108
+ ### GPL-Compatible Licenses (Quick)
109
+
110
+ - Safe defaults: GPL-2.0-or-later, GPL-3.0-or-later.
111
+ - Commonly accepted permissive families: MIT/Expat, BSD, ISC, zlib, Boost.
112
+ - Conditional compatibility requires care: Apache-2.0 and MPL-2.0 (verify usage context).
113
+ - For full accepted and rejected identifiers, use [gpl-compliance.md](references/gpl-compliance.md).
114
+
115
+ ### Common GPL Violations (Quick)
116
+
117
+ - Split licensing that restricts distributed code.
118
+ - Obfuscated or non-corresponding source distribution.
119
+ - Restrictive clauses (non-commercial, no-resale, forced backlink).
120
+ - Bundling GPL-incompatible libraries or assets.
121
+
122
+ ## Plugin Naming Rules (Guideline 17)
123
+
124
+ Use [naming-rules.md](references/naming-rules.md) for full trademark lists, slug blocks, and naming examples. Keep this inline checklist for quick screening.
125
+
126
+ ### Naming Checklist (Quick)
127
+
128
+ - Name is not a placeholder and has at least 5 alphanumeric characters.
129
+ - Header name and readme name match.
130
+ - Name is specific and function-related; avoid keyword stuffing.
131
+ - Trademark/project names appear only after connectors like `for`, `with`, `using`, `and`.
132
+ - No banned/discouraged terms or trademark portmanteaus.
133
+ - Slug is lowercase, hyphenated, <= 50 chars, and avoids blocked terms.
@@ -0,0 +1,217 @@
1
+ ## GPL Compliance (Guideline 1 in Detail)
2
+
3
+ ### Verification (Licensing)
4
+
5
+ - Every licensing-related issue must cite **Guideline 1** and include the specific file/license value found.
6
+ - License compatibility claims must match the GPL-Compatible Licenses table or the [GNU GPL-Compatible License List](https://www.gnu.org/licenses/license-list.html#GPLCompatibleLicenses).
7
+ - Use local `references/` files when present; otherwise use authoritative external URLs.
8
+
9
+ ### Failure modes (Licensing)
10
+
11
+ - If a license is not listed in the compatibility tables, do not guess; check the [GNU license list](https://www.gnu.org/licenses/license-list.html) or escalate.
12
+ - If a plugin uses a dual-license model, verify both licenses independently.
13
+
14
+
15
+ ### Quick Reference: WordPress GPL Requirements
16
+
17
+ WordPress is licensed under **GPLv2 or later**. All plugins distributed via WordPress.org must be:
18
+
19
+ 1. **100% GPL-compatible** (code, images, CSS, and all assets)
20
+ 2. Include a **license declaration** in the main plugin file header
21
+ 3. Include the **full license text** or a URI reference to it
22
+ 4. **Not restrict freedoms** granted by the GPL
23
+
24
+ ## GPL Versions Summary
25
+
26
+ | Version | Year | Key Addition |
27
+ |---------|------|--------------|
28
+ | GPLv1 | 1989 | Base copyleft: share-alike for modifications |
29
+ | GPLv2 | 1991 | Patent clause (Section 7), clearer distribution terms |
30
+ | GPLv3 | 2007 | Anti-tivoization, explicit patent grants, compatibility provisions |
31
+
32
+ WordPress uses **GPLv2 or later**, meaning plugins can use GPLv2, GPLv3, or "GPLv2 or later".
33
+
34
+ For full license texts, see:
35
+ - [GNU General Public License v1](https://www.gnu.org/licenses/gpl-1.0.html)
36
+ - [GNU General Public License v2](https://www.gnu.org/licenses/gpl-2.0.html)
37
+ - [GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.html)
38
+
39
+ ## License Compliance Checklist
40
+
41
+ When reviewing a plugin, verify:
42
+
43
+ - [ ] Main plugin file has a valid `License:` header (e.g., `GPL-2.0-or-later`, `GPL-2.0+`, `GPLv2 or later`)
44
+ - [ ] Main plugin file has a `License URI:` header pointing to the GPL text
45
+ - [ ] If bundled libraries exist, each has a GPL-compatible license
46
+ - [ ] No "split licensing" (e.g., code GPL but premium features proprietary)
47
+ - [ ] No additional restrictions beyond what GPL allows
48
+ - [ ] No clauses restricting commercial use, modification, or redistribution
49
+ - [ ] No obfuscated code (violates the spirit of source code availability)
50
+
51
+ ## Valid License Headers for WordPress Plugins
52
+
53
+ ```
54
+ License: GPL-2.0-or-later
55
+ License URI: https://www.gnu.org/licenses/gpl-2.0.html
56
+ ```
57
+
58
+ ```
59
+ License: GPL-3.0-or-later
60
+ License URI: https://www.gnu.org/licenses/gpl-3.0.html
61
+ ```
62
+
63
+ ```
64
+ License: GPLv2 or later
65
+ License URI: https://www.gnu.org/licenses/gpl-2.0.html
66
+ ```
67
+
68
+ ## Accepted Licenses by the WordPress.org Plugin Directory
69
+
70
+ Source: [Plugin Check - License_Utils trait](https://github.com/WordPress/plugin-check/blob/trunk/includes/Traits/License_Utils.php)
71
+
72
+ The Plugin Directory accepts licenses matching these identifiers (after normalization). The validation uses `is_license_gpl_compatible()` with the pattern:
73
+
74
+ ```
75
+ GPL|GNU|LGPL|MIT|FreeBSD|New BSD|BSD-3-Clause|BSD 3 Clause|OpenLDAP|Expat|Apache2|MPL20|ISC|CC0|Unlicense|WTFPL|Artistic|Boost|NCSA|ZLib|X11
76
+ ```
77
+
78
+ ### GPL Family (recommended)
79
+
80
+ | Accepted Values | SPDX Identifier | License URI |
81
+ |-----------------|-----------------|-------------|
82
+ | `GPL-2.0-or-later`, `GPLv2 or later`, `GPL-2.0+` | GPL-2.0-or-later | https://www.gnu.org/licenses/gpl-2.0.html |
83
+ | `GPL-2.0-only`, `GPLv2` | GPL-2.0-only | https://www.gnu.org/licenses/gpl-2.0.html |
84
+ | `GPL-3.0-or-later`, `GPLv3 or later`, `GPL-3.0+` | GPL-3.0-or-later | https://www.gnu.org/licenses/gpl-3.0.html |
85
+ | `GPL-3.0-only`, `GPLv3` | GPL-3.0-only | https://www.gnu.org/licenses/gpl-3.0.html |
86
+ | `GNU General Public License` (any version text) | — | — |
87
+ | `LGPL-2.1`, `LGPLv2.1` | LGPL-2.1-or-later | https://www.gnu.org/licenses/lgpl-2.1.html |
88
+ | `LGPL-3.0`, `LGPLv3` | LGPL-3.0-or-later | https://www.gnu.org/licenses/lgpl-3.0.html |
89
+
90
+ ### Other GPL-Compatible Licenses Accepted
91
+
92
+ | Identifier | License Name | Notes |
93
+ |------------|-------------|-------|
94
+ | `MIT` | MIT License | Permissive, compatible with GPLv2 and GPLv3 |
95
+ | `Expat` | Expat License | Functionally equivalent to MIT |
96
+ | `X11` | X11 License | Permissive; similar to Expat but with extra X Consortium clause |
97
+ | `FreeBSD` | BSD 2-Clause (FreeBSD) | Permissive, compatible with GPLv2 and GPLv3 |
98
+ | `New BSD`, `BSD-3-Clause`, `BSD 3 Clause` | BSD 3-Clause | Permissive, compatible with GPLv2 and GPLv3 |
99
+ | `Apache2`, `Apache-2.0` | Apache License 2.0 | Compatible with GPLv3 only (NOT GPLv2) |
100
+ | `MPL20`, `MPL-2.0` | Mozilla Public License 2.0 | Compatible via Section 3.3 |
101
+ | `ISC` | ISC License | Permissive, compatible with GPLv2 and GPLv3 |
102
+ | `OpenLDAP` | OpenLDAP Public License v2.7 | Permissive; older v2.3 is NOT compatible |
103
+ | `CC0` | Creative Commons Zero | Public domain dedication |
104
+ | `Unlicense` | The Unlicense | Public domain dedication |
105
+ | `WTFPL` | Do What The F*** You Want To Public License | Permissive, accepted in full text form too |
106
+ | `Artistic` | Artistic License 2.0 | Compatible via relicensing option in §4(c)(ii); Artistic 1.0 is NOT compatible |
107
+ | `Boost` | Boost Software License 1.0 | Lax permissive, compatible with GPLv2 and GPLv3 |
108
+ | `NCSA` | NCSA/University of Illinois Open Source License | Based on Expat + modified BSD; compatible with GPLv2 and GPLv3 |
109
+ | `ZLib` | zlib License | Permissive, compatible with GPLv2 and GPLv3 |
110
+
111
+ ### Licenses NOT Accepted
112
+
113
+ Any license not matching the identifiers above will be rejected. Common rejections include:
114
+
115
+ - **Proprietary / All Rights Reserved**
116
+ - **Creative Commons BY-NC** (NonCommercial restriction)
117
+ - **Creative Commons BY-ND** (NoDerivatives restriction)
118
+ - **Creative Commons BY-SA** (v3.0 and earlier; v4.0 is one-way compatible with GPLv3 but not in the Plugin Check regex)
119
+ - **JSON License** ("shall be used for Good, not Evil")
120
+ - **SSPL** (Server Side Public License)
121
+ - **BSL** (Business Source License)
122
+ - **Commons Clause**
123
+ - **Elastic License**
124
+ - **Original BSD (4-clause)** — advertising clause incompatible with GPL
125
+ - **MPL-1.0** — only MPL 2.0 is GPL-compatible
126
+ - **EPL** (Eclipse Public License) — weak copyleft, incompatible with GPL
127
+ - **EUPL** (European Union Public License) — copyleft incompatible with GPL without multi-step relicensing
128
+ - **Artistic License 1.0** — vague wording makes it incompatible; use 2.0 instead
129
+ - **OpenLDAP v2.3** (old) — incompatible; v2.7 is accepted
130
+
131
+ ## Common GPL Violations in Plugin Review
132
+
133
+ ### 1. Split Licensing
134
+ Plugin claims GPL but restricts premium features:
135
+ - "Free version is GPL, premium is proprietary" - **VIOLATION**
136
+ - All code distributed must be GPL-compatible
137
+
138
+ ### 2. Obfuscated Code
139
+ - Minified JavaScript is acceptable IF source is provided
140
+ - PHP obfuscation (ionCube, Zend Guard, etc.) - **VIOLATION** (prevents exercise of GPL freedoms)
141
+ - Encoded/encrypted PHP - **VIOLATION**
142
+
143
+ ### 3. Missing License Information
144
+ - No license header in main file
145
+ - No license file in the package
146
+ - Bundled libraries without license documentation
147
+
148
+ ### 4. Restrictive Clauses
149
+ - "You may not sell this plugin" - **VIOLATION** (GPL allows commercial redistribution)
150
+ - "You may not remove author credits" - Acceptable under GPLv3 Section 7(b), but not as blanket restriction
151
+ - "For personal use only" - **VIOLATION**
152
+ - "You must link back to our site" - **VIOLATION** (additional restriction)
153
+
154
+ ### 5. Incompatible Library Inclusion
155
+ - Including code under GPL-incompatible licenses
156
+ - Using assets (images, fonts, CSS) under restrictive licenses
157
+
158
+ ## Key GPL Concepts for Reviewers
159
+
160
+ ### Distribution vs. Private Use
161
+ - GPL obligations activate upon **distribution** (conveying to others)
162
+ - Private modifications do NOT trigger GPL requirements
163
+ - Publishing on WordPress.org IS distribution
164
+
165
+ ### Derivative Works
166
+ - A WordPress plugin that uses WordPress APIs is generally considered a derivative work
167
+ - Plugins that merely aggregate with WordPress may have different considerations
168
+ - When in doubt, the safe approach is GPL-compatible licensing
169
+
170
+ ### Source Code Requirement
171
+ - GPL requires access to "complete corresponding source code"
172
+ - For WordPress plugins: all PHP, JS source files, build scripts
173
+ - Minified files must have corresponding source available
174
+
175
+ ### The "Or Later" Clause
176
+ - "GPLv2 or later" allows users to choose GPLv2 OR any later version
177
+ - "GPLv2 only" means strictly GPLv2 (less flexible but valid)
178
+ - WordPress itself uses "GPLv2 or later"
179
+
180
+ ## Violation Reporting Workflow
181
+
182
+ When a GPL violation is identified:
183
+
184
+ 1. **Document the violation** precisely:
185
+ - Product name and version
186
+ - Distributor information
187
+ - Specific license terms violated
188
+ - Evidence (screenshots, code snippets)
189
+
190
+ 2. **Contact the copyright holder** first
191
+ 3. **Report to FSF** if the code is FSF-copyrighted: license-violation@gnu.org
192
+ 4. **For WordPress.org plugins**: flag through the plugin review process
193
+
194
+ ## Frequently Asked Questions
195
+
196
+ For comprehensive GPL FAQ answers, see the [GNU GPL FAQ](https://www.gnu.org/licenses/gpl-faq.html).
197
+
198
+ Common questions during plugin review:
199
+
200
+ **Can a plugin charge money and still be GPL?**
201
+ Yes. GPL allows charging for distribution. The requirement is that recipients get GPL freedoms (use, modify, redistribute).
202
+
203
+ **Does a plugin need to include the full GPL text?**
204
+ GPLv2 Section 1 and GPLv3 Section 4 require giving recipients a copy of the license. A URI reference in the header plus including a LICENSE file is standard practice.
205
+
206
+ **Can a plugin restrict who uses it?**
207
+ No. GPL explicitly prohibits additional restrictions on recipients. "For personal use only" or "non-commercial" clauses are incompatible.
208
+
209
+ **Is minified JS without source a violation?**
210
+ If the plugin only distributes minified JS without any way to obtain the source, this conflicts with GPL's source code requirements. The source should be available (in the package or via a repository).
211
+
212
+ **Can a plugin use CC-BY-SA images?**
213
+ CC-BY-SA 4.0 is one-way compatible with GPLv3 (CC-BY-SA material can be included in GPLv3 works). CC-BY-SA 3.0 is NOT compatible.
214
+
215
+ **What about fonts bundled in plugins?**
216
+ Fonts must be under GPL-compatible licenses. Common acceptable font licenses: OFL (SIL Open Font License), Apache 2.0 (with GPLv3), MIT, GPL with font exception.
217
+