@ai-kits/wp-ag-kit 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +12 -8
  2. package/STRUCTURE.md +9 -7
  3. package/agents/woocommerce-expert.md +32 -0
  4. package/agents/wordpress-expert.md +4 -2
  5. package/package.json +5 -3
  6. package/skills/woocommerce/SKILL.md +54 -0
  7. package/skills/woocommerce/references/backend-dev-guide.md +44 -0
  8. package/skills/woocommerce/references/code-entities.md +186 -0
  9. package/skills/woocommerce/references/code-quality.md +273 -0
  10. package/skills/woocommerce/references/code-review-guide.md +71 -0
  11. package/skills/woocommerce/references/coding-conventions.md +182 -0
  12. package/skills/woocommerce/references/copy-guidelines-guide.md +17 -0
  13. package/skills/woocommerce/references/data-integrity.md +164 -0
  14. package/skills/woocommerce/references/dependency-injection.md +102 -0
  15. package/skills/woocommerce/references/dev-cycle-guide.md +32 -0
  16. package/skills/woocommerce/references/file-entities.md +73 -0
  17. package/skills/woocommerce/references/hooks.md +87 -0
  18. package/skills/woocommerce/references/js-i18n-patterns.md +298 -0
  19. package/skills/woocommerce/references/markdown-guide.md +358 -0
  20. package/skills/woocommerce/references/markdown-linting.md +202 -0
  21. package/skills/woocommerce/references/php-i18n-patterns.md +83 -0
  22. package/skills/woocommerce/references/php-linting-patterns.md +304 -0
  23. package/skills/woocommerce/references/running-tests.md +249 -0
  24. package/skills/woocommerce/references/security-patterns.md +109 -0
  25. package/skills/woocommerce/references/sentence-case.md +177 -0
  26. package/skills/woocommerce/references/type-annotations.md +161 -0
  27. package/skills/woocommerce/references/unit-tests.md +362 -0
  28. package/skills/woocommerce/references/woocommerce-global-objects.md +89 -0
@@ -0,0 +1,102 @@
1
+ # Dependency Injection
2
+
3
+ ## Table of Contents
4
+
5
+ - [Standard DI Pattern for `src/` Classes](#standard-di-pattern-for-src-classes)
6
+ - [Initializing Classes That Set Up Hooks](#initializing-classes-that-set-up-hooks)
7
+ - [Using the Container to Get Instances](#using-the-container-to-get-instances)
8
+ - [Why Use Dependency Injection?](#why-use-dependency-injection)
9
+
10
+ ## Standard DI Pattern for `src/` Classes
11
+
12
+ Dependencies are injected via a `final` `init` method with `@internal` annotation (blank lines before/after).
13
+
14
+ **Example:**
15
+
16
+ ```php
17
+ namespace Automattic\WooCommerce\Internal\Admin;
18
+
19
+ use Automattic\WooCommerce\Internal\Logging\Logger;
20
+ use Automattic\WooCommerce\Internal\DataStore\OrderDataStore;
21
+
22
+ class OrderProcessor {
23
+ private Logger $logger;
24
+ private OrderDataStore $data_store;
25
+
26
+ /**
27
+ * Initialize the order processor with dependencies.
28
+ *
29
+ * @internal
30
+ *
31
+ * @param Logger $logger The logger instance.
32
+ * @param OrderDataStore $data_store The order data store.
33
+ */
34
+ final public function init( Logger $logger, OrderDataStore $data_store ) {
35
+ $this->logger = $logger;
36
+ $this->data_store = $data_store;
37
+ }
38
+
39
+ public function process( int $order_id ) {
40
+ $this->logger->log( "Processing order {$order_id}" );
41
+ // ...
42
+ }
43
+ }
44
+ ```
45
+
46
+ ## Initializing Classes That Set Up Hooks
47
+
48
+ Add to `includes/class-woocommerce.php` in `init_hooks()`:
49
+
50
+ - Use `$container->get( ClassName::class );`
51
+ - Add at end of "These classes set up hooks on instantiation" section
52
+
53
+ **Example in `includes/class-woocommerce.php`:**
54
+
55
+ ```php
56
+ private function init_hooks() {
57
+ // ... existing code ...
58
+
59
+ // These classes set up hooks on instantiation
60
+ $container->get( SomeExistingClass::class );
61
+ $container->get( AnotherExistingClass::class );
62
+ $container->get( YourNewClass::class ); // Add your new class here
63
+ }
64
+ ```
65
+
66
+ ## Using the Container to Get Instances
67
+
68
+ When you need to get an instance of a class from the container elsewhere in the code:
69
+
70
+ ```php
71
+ use Automattic\WooCommerce\Internal\Utils\DataParser;
72
+
73
+ // Get instance from container
74
+ $parser = wc_get_container()->get( DataParser::class );
75
+ ```
76
+
77
+ ### Singleton Behavior
78
+
79
+ **Important:** The container always retrieves the same instance of a given class (singleton pattern).
80
+
81
+ When different instances are needed (and only in this case), use `new` or the appropriate factory methods for the class when available.
82
+
83
+ **Example:**
84
+
85
+ ```php
86
+ // Same instance every time - use container
87
+ $logger = wc_get_container()->get( Logger::class );
88
+ $same_logger = wc_get_container()->get( Logger::class ); // Same instance as above
89
+
90
+ // Different instances needed - use new or factory
91
+ $order1 = new WC_Order( 123 );
92
+ $order2 = new WC_Order( 456 ); // Different instance
93
+
94
+ // Using factory when available
95
+ $product = wc_get_product( 789 ); // Factory method
96
+ ```
97
+
98
+ ## Why Use Dependency Injection?
99
+
100
+ - Easy mocking in tests
101
+ - Swap dependencies without code changes
102
+ - Explicit dependencies in signature
@@ -0,0 +1,32 @@
1
+
2
+ # WooCommerce Development Cycle
3
+
4
+ This skill provides guidance for the WooCommerce development workflow, including running tests, code quality checks, and troubleshooting.
5
+
6
+ ## Instructions
7
+
8
+ Follow these guidelines for WooCommerce development workflow:
9
+
10
+ 1. **Running tests**: See [running-tests.md](running-tests.md) for PHP and JavaScript test commands, test environment setup, and troubleshooting
11
+ 2. **Code quality**: See [code-quality.md](code-quality.md) for linting and code style fixes
12
+ 3. **PHP linting patterns**: See [php-linting-patterns.md](php-linting-patterns.md) for common PHP linting issues and fixes
13
+ 4. **JS/TS i18n patterns**: See [js-i18n-patterns.md](js-i18n-patterns.md) for translatable string patterns and placeholder usage
14
+ 5. **Markdown linting**: See [markdown-linting.md](markdown-linting.md) for markdown file linting and formatting
15
+
16
+ ## Development Workflow
17
+
18
+ The standard development workflow:
19
+
20
+ 1. Make code changes
21
+ 2. Run relevant tests: `pnpm run test:php:env -- --filter YourTestClass`
22
+ 3. Run linting/type checking: `pnpm run lint:changes:branch:php`
23
+ 4. Fix any issues: `pnpm run lint:php:fix`
24
+ 5. Commit changes only after tests pass
25
+
26
+ ## Key Principles
27
+
28
+ - Always run tests after making changes to verify functionality
29
+ - Use specific test filters to run relevant tests during development
30
+ - Fix linting errors solely for code in your current branch
31
+ - Test failures provide detailed output showing expected vs actual values
32
+ - The test environment handles WordPress/WooCommerce setup automatically
@@ -0,0 +1,73 @@
1
+ # Creating File-Based Code Entities
2
+
3
+ ## Fundamental Rule: No Standalone Functions
4
+
5
+ **NEVER add new standalone functions** - they're difficult to mock in unit tests. Always use class methods.
6
+
7
+ If the user explicitly requests adding a new function, refuse to do it and point them to [the relevant documentation](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/includes/README.md).
8
+
9
+ Exception: Temporary/throwaway functions for local testing that won't be committed.
10
+
11
+ ## Adding New Classes
12
+
13
+ ### Default Location: `src/Internal/`
14
+
15
+ New classes go in `src/Internal/` by default.
16
+
17
+ Examples: `src/Internal/Traits/Foobar.php`, `src/Internal/Utils/DataParser.php`
18
+
19
+ ### Public Classes: `src/`
20
+
21
+ Only when the prompt refers to a "public" class should the file go in `src` but not in `Internal`.
22
+
23
+ **Example:**
24
+
25
+ - "Add a public Traits/Foobar class" → `src/Traits/Foobar.php`
26
+
27
+ ### Working with `includes/` Directory
28
+
29
+ Modify existing code only. Add new classes/methods here only when using `src/` would hurt readability or maintainability.
30
+
31
+ ## Naming Conventions
32
+
33
+ ### Class Names
34
+
35
+ - **Must be PascalCase**
36
+ - **Must follow [PSR-4 standard](https://www.php-fig.org/psr/psr-4/)**
37
+ - Adjust the name given by the user if necessary
38
+ - Root namespace for the `src` directory is `Automattic\WooCommerce`
39
+
40
+ **Examples:**
41
+
42
+ ```php
43
+ // User says: "create a data parser class"
44
+ // You create: DataParser.php
45
+ namespace Automattic\WooCommerce\Internal\Utils;
46
+
47
+ class DataParser {
48
+ // ...
49
+ }
50
+ ```
51
+
52
+ ## Namespace and Import Conventions
53
+
54
+ When referencing a namespaced class:
55
+
56
+ 1. Always add a `use` statement with the fully qualified class name at the beginning of the file
57
+ 2. Reference the short class name throughout the code
58
+
59
+ **Good:**
60
+
61
+ ```php
62
+ use Automattic\WooCommerce\Internal\Utils\Foobar;
63
+
64
+ // Later in code:
65
+ $instance = $container->get( Foobar::class );
66
+ ```
67
+
68
+ **Avoid:**
69
+
70
+ ```php
71
+ // No use statement, using fully qualified name:
72
+ $instance = $container->get( \Automattic\WooCommerce\Internal\Utils\Foobar::class );
73
+ ```
@@ -0,0 +1,87 @@
1
+ # Working with Hooks
2
+
3
+ ## Hook Callback Naming Convention
4
+
5
+ Name hook callback methods: `handle_{hook_name}` with `@internal` annotation.
6
+
7
+ **Examples:**
8
+
9
+ ```php
10
+ /**
11
+ * Handle the woocommerce_init hook.
12
+ *
13
+ * @internal
14
+ */
15
+ public function handle_woocommerce_init() {
16
+ // Initialize components
17
+ }
18
+
19
+ /**
20
+ * Handle the woocommerce_before_checkout hook.
21
+ *
22
+ * @internal
23
+ *
24
+ * @param WC_Checkout $checkout The checkout object.
25
+ */
26
+ public function handle_woocommerce_before_checkout( $checkout ) {
27
+ // Setup checkout process
28
+ }
29
+ ```
30
+
31
+ ## Hook Docblocks
32
+
33
+ If you modify a line that fires a hook without a docblock:
34
+
35
+ 1. Add docblock with description and `@param` tags
36
+ 2. Use `git log -S "hook_name"` to find when it was introduced
37
+ 3. Add `@since` annotation with that version
38
+
39
+ ```php
40
+ /**
41
+ * Fires after an order has been processed.
42
+ *
43
+ * @param int $order_id The processed order ID.
44
+ * @param array $order_data The order data.
45
+ *
46
+ * @since 8.2.0
47
+ */
48
+ do_action( 'woocommerce_order_processed', $order_id, $order_data );
49
+ ```
50
+
51
+ ## Hook Documentation Requirements
52
+
53
+ All hooks must have docblocks that include:
54
+
55
+ - Description of when the hook fires
56
+ - `@param` tags for each parameter passed to the hook
57
+ - `@since` annotation with the version number (last line, with blank line before)
58
+ - For new hooks: Use the version from `includes/class-woocommerce.php` on trunk, removing `-dev` suffix
59
+ - For existing hooks: Use `git log -S "hook_name"` to find when it was introduced
60
+
61
+ **Action hook example:**
62
+
63
+ ```php
64
+ /**
65
+ * Fires after a product is saved.
66
+ *
67
+ * @param int $product_id The product ID.
68
+ * @param WC_Product $product The product object.
69
+ *
70
+ * @since 9.5.0
71
+ */
72
+ do_action( 'woocommerce_product_saved', $product_id, $product );
73
+ ```
74
+
75
+ **Filter hook example:**
76
+
77
+ ```php
78
+ /**
79
+ * Filters the product price before display.
80
+ *
81
+ * @param string $price The formatted price.
82
+ * @param WC_Product $product The product object.
83
+ *
84
+ * @since 9.5.0
85
+ */
86
+ $price = apply_filters( 'woocommerce_product_price', $price, $product );
87
+ ```
@@ -0,0 +1,298 @@
1
+ # JavaScript/TypeScript i18n Patterns
2
+
3
+ ## Table of Contents
4
+
5
+ - [Overview](#overview)
6
+ - [Translation Functions](#translation-functions)
7
+ - [Placeholder Patterns](#placeholder-patterns)
8
+ - [Translator Comments](#translator-comments)
9
+ - [Complex String Patterns](#complex-string-patterns)
10
+ - [Common Pitfalls](#common-pitfalls)
11
+ - [Quick Command Reference](#quick-command-reference)
12
+
13
+ ## Overview
14
+
15
+ WooCommerce uses WordPress i18n functions from `@wordpress/i18n` for
16
+ translatable strings. Brand names like "WooPayments" should use placeholders
17
+ to improve translation flexibility.
18
+
19
+ ```typescript
20
+ import { __, sprintf } from '@wordpress/i18n';
21
+ ```
22
+
23
+ ## Translation Functions
24
+
25
+ ### Basic Translation
26
+
27
+ ```typescript
28
+ // Simple string
29
+ __( 'Save changes', 'woocommerce' )
30
+
31
+ // String with placeholder
32
+ sprintf(
33
+ /* translators: %s: Payment provider name (e.g., WooPayments) */
34
+ __( 'Set up %s', 'woocommerce' ),
35
+ 'WooPayments'
36
+ )
37
+ ```
38
+
39
+ ### Plural Forms with `_n`
40
+
41
+ ```typescript
42
+ import { _n, sprintf } from '@wordpress/i18n';
43
+
44
+ sprintf(
45
+ /* translators: %d: Number of items */
46
+ _n(
47
+ '%d item selected',
48
+ '%d items selected',
49
+ count,
50
+ 'woocommerce'
51
+ ),
52
+ count
53
+ )
54
+ ```
55
+
56
+ ### Interpolated Elements with `createInterpolateElement`
57
+
58
+ ```typescript
59
+ import { createInterpolateElement } from '@wordpress/element';
60
+ import { __, sprintf } from '@wordpress/i18n';
61
+
62
+ createInterpolateElement(
63
+ sprintf(
64
+ /* translators: 1: Payment provider name */
65
+ __( 'Enable <strong>%1$s</strong> for your store.', 'woocommerce' ),
66
+ 'WooPayments'
67
+ ),
68
+ {
69
+ strong: <strong />,
70
+ }
71
+ )
72
+ ```
73
+
74
+ ## Placeholder Patterns
75
+
76
+ ### Single Placeholder
77
+
78
+ Use `%s` for a single placeholder:
79
+
80
+ ```typescript
81
+ sprintf(
82
+ /* translators: %s: Payment provider name (e.g., WooPayments) */
83
+ __( 'Get paid with %s', 'woocommerce' ),
84
+ 'WooPayments'
85
+ )
86
+ ```
87
+
88
+ ### Multiple Same Placeholders
89
+
90
+ Use numbered placeholders `%1$s` when the same value appears multiple times:
91
+
92
+ ```typescript
93
+ sprintf(
94
+ /* translators: 1: Payment provider name (e.g., WooPayments) */
95
+ __(
96
+ 'By using %1$s you agree to our Terms. Payments via %1$s are secure.',
97
+ 'woocommerce'
98
+ ),
99
+ 'WooPayments'
100
+ )
101
+ ```
102
+
103
+ ### Multiple Different Placeholders
104
+
105
+ Use numbered placeholders `%1$s`, `%2$s` for different values:
106
+
107
+ ```typescript
108
+ sprintf(
109
+ /* translators: 1: Payment provider name, 2: Extension names */
110
+ _n(
111
+ 'Installing %1$s will activate %2$s extension.',
112
+ 'Installing %1$s will activate %2$s extensions.',
113
+ extensionCount,
114
+ 'woocommerce'
115
+ ),
116
+ 'WooPayments',
117
+ extensionNames
118
+ )
119
+ ```
120
+
121
+ ## Translator Comments
122
+
123
+ ### Comment Placement
124
+
125
+ The translator comment must be placed **immediately before the `__()` or
126
+ `_n()` function**, not before `sprintf()`:
127
+
128
+ ```typescript
129
+ // ❌ WRONG - Comment before sprintf
130
+ /* translators: %s: Payment provider name */
131
+ sprintf(
132
+ __( 'Set up %s', 'woocommerce' ),
133
+ 'WooPayments'
134
+ )
135
+
136
+ // ✅ CORRECT - Comment inside sprintf, before __()
137
+ sprintf(
138
+ /* translators: %s: Payment provider name */
139
+ __( 'Set up %s', 'woocommerce' ),
140
+ 'WooPayments'
141
+ )
142
+ ```
143
+
144
+ ### Comment Format for Numbered Placeholders
145
+
146
+ When using numbered placeholders like `%1$s`, use just the number in the comment:
147
+
148
+ ```typescript
149
+ // ❌ WRONG - Using %1$s in comment
150
+ /* translators: %1$s: Provider name, %2$s: Country */
151
+
152
+ // ✅ CORRECT - Using just numbers
153
+ /* translators: 1: Provider name, 2: Country */
154
+ ```
155
+
156
+ ### Descriptive Comments
157
+
158
+ Always provide context for translators:
159
+
160
+ ```typescript
161
+ // ❌ WRONG - No context
162
+ /* translators: %s: name */
163
+
164
+ // ✅ CORRECT - Clear context
165
+ /* translators: %s: Payment provider name (e.g., WooPayments) */
166
+ ```
167
+
168
+ ## Complex String Patterns
169
+
170
+ ### Combining `sprintf`, `_n`, and `createInterpolateElement`
171
+
172
+ ```typescript
173
+ installText: ( extensionsString: string ) => {
174
+ const count = extensionsString.split( ', ' ).length;
175
+ return createInterpolateElement(
176
+ sprintf(
177
+ /* translators: 1: Provider name, 2: Extension names */
178
+ _n(
179
+ 'Installing <strong>%1$s</strong> activates <strong>%2$s</strong>.',
180
+ 'Installing <strong>%1$s</strong> activates <strong>%2$s</strong>.',
181
+ count,
182
+ 'woocommerce'
183
+ ),
184
+ 'WooPayments',
185
+ extensionsString
186
+ ),
187
+ { strong: <strong /> }
188
+ );
189
+ }
190
+ ```
191
+
192
+ ### Strings with Links
193
+
194
+ ```typescript
195
+ createInterpolateElement(
196
+ sprintf(
197
+ /* translators: 1: Payment provider name */
198
+ __(
199
+ 'Learn more about <a>%1$s</a> features.',
200
+ 'woocommerce'
201
+ ),
202
+ 'WooPayments'
203
+ ),
204
+ {
205
+ a: (
206
+ <a
207
+ href="https://example.com"
208
+ target="_blank"
209
+ rel="noopener noreferrer"
210
+ />
211
+ ),
212
+ }
213
+ )
214
+ ```
215
+
216
+ ## Common Pitfalls
217
+
218
+ ### Curly Apostrophes
219
+
220
+ TypeScript files may use curly apostrophes (`'` U+2019) instead of straight
221
+ apostrophes (`'` U+0027). When editing, preserve the original character:
222
+
223
+ ```typescript
224
+ // Original uses curly apostrophe - preserve it
225
+ __( 'I don't want to install another plugin', 'woocommerce' )
226
+ // ^ This is U+2019, not U+0027
227
+ ```
228
+
229
+ ### ESLint i18n Rules
230
+
231
+ The `@wordpress/i18n-translator-comments` rule requires comments directly
232
+ before the translation function:
233
+
234
+ ```typescript
235
+ // ❌ ESLint error - comment not adjacent to __()
236
+ const title = sprintf(
237
+ /* translators: %s: Provider name */
238
+
239
+ __( 'Set up %s', 'woocommerce' ),
240
+ 'WooPayments'
241
+ );
242
+
243
+ // ✅ Correct - comment directly before __()
244
+ const title = sprintf(
245
+ /* translators: %s: Provider name */
246
+ __( 'Set up %s', 'woocommerce' ),
247
+ 'WooPayments'
248
+ );
249
+ ```
250
+
251
+ ### Brand Names
252
+
253
+ Always use placeholders for brand names to improve translation flexibility:
254
+
255
+ ```typescript
256
+ // ✅ CORRECT - Use placeholder for brand name
257
+ title: sprintf(
258
+ /* translators: %s: Payment provider name */
259
+ __( 'Get paid with %s', 'woocommerce' ),
260
+ 'WooPayments'
261
+ )
262
+
263
+ // ✅ CORRECT - Use placeholder in descriptions
264
+ description: sprintf(
265
+ /* translators: %s: Payment provider name */
266
+ __( 'Enable PayPal alongside %s', 'woocommerce' ),
267
+ 'WooPayments'
268
+ )
269
+
270
+ // ❌ WRONG - Hardcoded brand name
271
+ title: __( 'Get paid with WooPayments', 'woocommerce' )
272
+ ```
273
+
274
+ ## Quick Command Reference
275
+
276
+ ```bash
277
+ # Lint specific file
278
+ npx eslint client/path/to/file.tsx
279
+
280
+ # Fix specific file
281
+ npx eslint --fix client/path/to/file.tsx
282
+
283
+ # Type check
284
+ pnpm run ts:check
285
+
286
+ # ❌ NEVER lint entire codebase
287
+ pnpm run lint # NO - lints everything
288
+ ```
289
+
290
+ ## Summary
291
+
292
+ | Pattern | Example |
293
+ |-------------------------------|----------------------------------------------|
294
+ | **Single placeholder** | `sprintf( __( 'Set up %s' ), 'Name' )` |
295
+ | **Repeated placeholder** | `sprintf( __( '%1$s via %1$s' ), 'Name' )` |
296
+ | **Multiple placeholders** | `sprintf( __( '%1$s in %2$s' ), 'A', 'B' )` |
297
+ | **Comment format (simple)** | `/* translators: %s: Provider name */` |
298
+ | **Comment format (numbered)** | `/* translators: 1: Provider, 2: Country */` |