ima-claude 2.20.0 → 2.26.0

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 (80) hide show
  1. package/README.md +74 -9
  2. package/dist/cli.js +2 -1
  3. package/package.json +1 -1
  4. package/plugins/ima-claude/.claude-plugin/plugin.json +2 -2
  5. package/plugins/ima-claude/agents/explorer.md +29 -15
  6. package/plugins/ima-claude/agents/implementer.md +58 -13
  7. package/plugins/ima-claude/agents/memory.md +19 -19
  8. package/plugins/ima-claude/agents/reviewer.md +84 -34
  9. package/plugins/ima-claude/agents/tester.md +59 -16
  10. package/plugins/ima-claude/agents/wp-developer.md +66 -21
  11. package/plugins/ima-claude/hooks/bootstrap.sh +42 -44
  12. package/plugins/ima-claude/hooks/prompt_coach_digest.md +14 -17
  13. package/plugins/ima-claude/hooks/prompt_coach_system.md +10 -12
  14. package/plugins/ima-claude/personalities/README.md +17 -6
  15. package/plugins/ima-claude/personalities/enable-efficient.md +61 -0
  16. package/plugins/ima-claude/personalities/enable-terse.md +71 -0
  17. package/plugins/ima-claude/skills/agentic-workflows/SKILL.md +35 -71
  18. package/plugins/ima-claude/skills/architect/SKILL.md +54 -168
  19. package/plugins/ima-claude/skills/compound-bridge/SKILL.md +41 -94
  20. package/plugins/ima-claude/skills/design-to-code/SKILL.md +43 -78
  21. package/plugins/ima-claude/skills/discourse/SKILL.md +79 -194
  22. package/plugins/ima-claude/skills/discourse-admin/SKILL.md +41 -103
  23. package/plugins/ima-claude/skills/docs-organize/SKILL.md +63 -203
  24. package/plugins/ima-claude/skills/ember-discourse/SKILL.md +90 -200
  25. package/plugins/ima-claude/skills/espocrm/SKILL.md +14 -23
  26. package/plugins/ima-claude/skills/espocrm-api/SKILL.md +79 -192
  27. package/plugins/ima-claude/skills/functional-programmer/SKILL.md +33 -237
  28. package/plugins/ima-claude/skills/gh-cli/SKILL.md +26 -65
  29. package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +71 -104
  30. package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +32 -22
  31. package/plugins/ima-claude/skills/ima-brand/SKILL.md +18 -23
  32. package/plugins/ima-claude/skills/ima-copywriting/SKILL.md +68 -179
  33. package/plugins/ima-claude/skills/ima-doc2pdf/SKILL.md +32 -102
  34. package/plugins/ima-claude/skills/ima-editorial-scorecard/SKILL.md +38 -63
  35. package/plugins/ima-claude/skills/ima-editorial-workflow/SKILL.md +69 -114
  36. package/plugins/ima-claude/skills/ima-email-creator/SKILL.md +16 -22
  37. package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +21 -37
  38. package/plugins/ima-claude/skills/ima-git/SKILL.md +81 -0
  39. package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +39 -120
  40. package/plugins/ima-claude/skills/jquery/SKILL.md +107 -233
  41. package/plugins/ima-claude/skills/js-fp/SKILL.md +75 -296
  42. package/plugins/ima-claude/skills/js-fp-api/SKILL.md +52 -162
  43. package/plugins/ima-claude/skills/js-fp-react/SKILL.md +47 -270
  44. package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +55 -209
  45. package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +59 -204
  46. package/plugins/ima-claude/skills/livecanvas/SKILL.md +19 -32
  47. package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +92 -162
  48. package/plugins/ima-claude/skills/mcp-context7/SKILL.md +32 -64
  49. package/plugins/ima-claude/skills/mcp-gitea/SKILL.md +98 -188
  50. package/plugins/ima-claude/skills/mcp-github/SKILL.md +60 -124
  51. package/plugins/ima-claude/skills/mcp-memory/SKILL.md +1 -177
  52. package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +58 -115
  53. package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +32 -87
  54. package/plugins/ima-claude/skills/mcp-serena/SKILL.md +54 -80
  55. package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +40 -63
  56. package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +75 -116
  57. package/plugins/ima-claude/skills/php-authnet/SKILL.md +32 -65
  58. package/plugins/ima-claude/skills/php-fp/SKILL.md +50 -129
  59. package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +25 -73
  60. package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +103 -463
  61. package/plugins/ima-claude/skills/playwright/SKILL.md +69 -220
  62. package/plugins/ima-claude/skills/prompt-starter/SKILL.md +33 -83
  63. package/plugins/ima-claude/skills/prompt-starter/references/code-review.md +38 -0
  64. package/plugins/ima-claude/skills/py-fp/SKILL.md +78 -384
  65. package/plugins/ima-claude/skills/quasar-fp/SKILL.md +54 -255
  66. package/plugins/ima-claude/skills/quickstart/SKILL.md +7 -11
  67. package/plugins/ima-claude/skills/rails/SKILL.md +63 -184
  68. package/plugins/ima-claude/skills/resume-session/SKILL.md +14 -35
  69. package/plugins/ima-claude/skills/rg/SKILL.md +61 -146
  70. package/plugins/ima-claude/skills/ruby-fp/SKILL.md +66 -163
  71. package/plugins/ima-claude/skills/save-session/SKILL.md +10 -39
  72. package/plugins/ima-claude/skills/scorecard/SKILL.md +42 -40
  73. package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +42 -71
  74. package/plugins/ima-claude/skills/skill-creator/SKILL.md +79 -250
  75. package/plugins/ima-claude/skills/task-master/SKILL.md +11 -31
  76. package/plugins/ima-claude/skills/task-planner/SKILL.md +44 -153
  77. package/plugins/ima-claude/skills/task-runner/SKILL.md +61 -143
  78. package/plugins/ima-claude/skills/unit-testing/SKILL.md +59 -134
  79. package/plugins/ima-claude/skills/wp-ddev/SKILL.md +38 -120
  80. package/plugins/ima-claude/skills/wp-local/SKILL.md +26 -108
@@ -16,95 +16,64 @@ description: >-
16
16
 
17
17
  **"jQuery IS native in WordPress. Reach for it first."**
18
18
 
19
- ## Why This Skill Exists
19
+ Agents default to verbose vanilla JS even when jQuery is loaded. In WordPress, jQuery is always available (core dependency, 0 additional bytes). `$('.foo').on('click', ...)` beats `document.querySelectorAll('.foo').forEach(el => el.addEventListener('click', ...))`.
20
20
 
21
- Agents default to verbose vanilla JS even when jQuery is loaded and simpler. In WordPress, jQuery is **always available** (core dependency, 0 additional bytes). Writing `document.querySelectorAll('.foo').forEach(el => el.addEventListener('click', ...))` when `$('.foo').on('click', ...)` exists is unnecessary complexity.
22
-
23
- **This skill ensures jQuery is the default for DOM work in WordPress/Bootstrap environments.**
24
-
25
- ## When to Use jQuery (Decision Tree)
21
+ ## Decision Tree
26
22
 
27
23
  ```
28
- Writing browser JS in a WordPress environment?
29
- ├── YES: Does it touch the DOM (select, manipulate, events, AJAX)?
30
- │ ├── YES → Use jQuery (default choice)
31
- │ │ Exception: Pure business logic (calculations, validation, formatting)
32
- │ → Keep as vanilla JS in pure/ directory (testable without DOM)
33
- │ └── NO (pure data transforms, utilities) → Vanilla JS
34
- ├── Is jQuery already loaded on the page?
24
+ Writing browser JS in WordPress?
25
+ ├── YES: Touches DOM (select, manipulate, events, AJAX)?
26
+ │ ├── YES → Use jQuery (default)
27
+ │ │ Exception: Pure business logic vanilla JS in pure/ directory
28
+ └── NO (pure data transforms) Vanilla JS
29
+ ├── jQuery already loaded?
35
30
  │ ├── YES → Use jQuery for DOM work
36
- │ └── NO → Vanilla JS (don't add jQuery just for convenience)
37
- └── NO WordPress context?
38
- └── See js-fp for vanilla patterns
31
+ │ └── NO → Vanilla JS
32
+ └── No WordPress context → See js-fp
39
33
  ```
40
34
 
41
- **Strong signals to use jQuery:**
42
- - WordPress theme or plugin JavaScript
43
- - Bootstrap component initialization or interaction
44
- - Gravity Forms, ACF, or any jQuery-based plugin integration
45
- - AJAX calls to `admin-ajax.php` or WP REST API
46
- - Event delegation on dynamic content
47
- - DOM traversal and manipulation
35
+ **Use jQuery:** WordPress theme/plugin JS, Bootstrap component interaction, Gravity Forms/ACF integration, `admin-ajax.php` AJAX, event delegation on dynamic content.
48
36
 
49
- **Signals to use vanilla JS instead:**
50
- - Pure business logic (no DOM)
51
- - Isolated ES module with no WP plugin interaction
52
- - Node.js / server-side code
53
- - React/Vue component internals
37
+ **Use vanilla JS:** Pure business logic, isolated ES module, Node.js, React/Vue internals.
54
38
 
55
39
  ## jQuery + FP: They're Compatible
56
40
 
57
- jQuery's API is inherently functional in several ways:
58
-
59
41
  ### Chaining IS Composition
60
42
 
61
43
  ```javascript
62
- // jQuery chaining = function composition without pipe()
63
44
  $('.user-card')
64
45
  .filter('.active')
65
46
  .find('.username')
66
47
  .addClass('highlighted')
67
48
  .text(function(i, text) { return text.toUpperCase(); });
68
-
69
- // Each method takes input, returns output (the jQuery object)
70
- // This IS composition — no custom pipe() needed
49
+ // No custom pipe() needed
71
50
  ```
72
51
 
73
- ### $.map and $.grep ARE Declarative
52
+ ### $.map / $.grep Are Declarative
74
53
 
75
54
  ```javascript
76
- // jQuery's functional utilities
77
- var activeNames = $.map($('.user'), function(el) {
78
- return $(el).data('active') ? $(el).text() : null;
79
- });
80
-
81
- // $.grep = filter
82
- var admins = $.grep(users, function(user) {
83
- return user.role === 'admin';
84
- });
55
+ // Use for jQuery collections
56
+ var admins = $.grep(users, function(user) { return user.role === 'admin'; });
85
57
 
86
- // Prefer native Array methods for plain data:
58
+ // Use native Array methods for plain arrays
87
59
  var activeNames = users.filter(u => u.active).map(u => u.name);
88
- // Use $.map/$.grep when working with jQuery collections
89
60
  ```
90
61
 
91
- ### Pure Logic Extraction (The FP Core)
62
+ ### Pure Logic Extraction
92
63
 
93
64
  ```javascript
94
65
  (function($) {
95
66
  'use strict';
96
67
 
97
- // PURE: Business logic — testable, no DOM
68
+ // PURE: Testable, no DOM
98
69
  function calculateShipping(weight, zone) {
99
70
  var rates = { domestic: 0.5, international: 1.2 };
100
71
  return Math.max(0, weight) * (rates[zone] || rates.domestic);
101
72
  }
102
73
 
103
- function formatPrice(amount) {
104
- return '$' + Math.max(0, amount).toFixed(2);
105
- }
74
+ function formatPrice(amount) { return '$' + Math.max(0, amount).toFixed(2); }
106
75
 
107
- // IMPURE: DOM wrapper — uses jQuery, calls pure functions
76
+ // IMPURE: DOM wrapper
108
77
  function ShippingCalculator($container) {
109
78
  this.$container = $container;
110
79
  this.$container.on('change', 'select, input', this.update.bind(this));
@@ -113,18 +82,13 @@ var activeNames = users.filter(u => u.active).map(u => u.name);
113
82
  ShippingCalculator.prototype.update = function() {
114
83
  var weight = parseFloat(this.$container.find('[name="weight"]').val()) || 0;
115
84
  var zone = this.$container.find('[name="zone"]').val();
116
- var cost = calculateShipping(weight, zone);
117
- this.$container.find('.shipping-cost').text(formatPrice(cost));
85
+ this.$container.find('.shipping-cost').text(formatPrice(calculateShipping(weight, zone)));
118
86
  };
119
87
 
120
- // Init
121
- $('.shipping-calculator').each(function() {
122
- new ShippingCalculator($(this));
123
- });
88
+ $('.shipping-calculator').each(function() { new ShippingCalculator($(this)); });
124
89
 
125
- // Export pure functions for testing
126
90
  if (typeof module !== 'undefined' && module.exports) {
127
- module.exports = { calculateShipping: calculateShipping, formatPrice: formatPrice };
91
+ module.exports = { calculateShipping, formatPrice };
128
92
  }
129
93
  })(jQuery);
130
94
  ```
@@ -134,182 +98,115 @@ var activeNames = users.filter(u => u.active).map(u => u.name);
134
98
  ### IIFE Wrapper (WordPress Standard)
135
99
 
136
100
  ```javascript
137
- // Always use this pattern in WordPress — avoids $ conflicts
138
101
  (function($) {
139
102
  'use strict';
140
-
141
103
  // All jQuery code here, $ is safe
142
-
143
104
  })(jQuery);
144
105
 
145
- // Shorthand document ready
146
- jQuery(function($) {
147
- // DOM ready, $ is safe
148
- });
106
+ // Document ready shorthand
107
+ jQuery(function($) { /* DOM ready */ });
149
108
  ```
150
109
 
151
110
  ### Selectors and Traversal
152
111
 
153
112
  ```javascript
154
- // Selecting
155
113
  $('.class') // By class
156
114
  $('#id') // By ID
157
115
  $('[data-action="delete"]') // By attribute
158
- $('.parent .child') // Descendant
159
- $('.item:first') // Pseudo-selector
160
116
  $('input[type="text"]') // Attribute selector
161
117
 
162
- // Traversal (chained)
163
118
  $('.item')
164
- .closest('.container') // Up: nearest ancestor matching selector
165
- .find('.target') // Down: descendants matching selector
166
- .siblings('.active') // Sideways: siblings matching selector
119
+ .closest('.container') // Up: nearest ancestor
120
+ .find('.target') // Down: all descendants
121
+ .siblings('.active') // Sideways: siblings
167
122
  .parent() // Up: direct parent
168
- .children('.row') // Down: direct children only
169
- .first() // Filter: first in set
170
- .filter('.visible') // Filter: matching selector
171
- .not('.disabled') // Filter: exclude matching
172
- .eq(2) // Filter: by index
123
+ .children('.row') // Down: direct children
124
+ .first() / .last()
125
+ .filter('.visible')
126
+ .not('.disabled')
127
+ .eq(2) // By index
173
128
 
174
- // Context-scoped selection (efficient)
175
129
  var $form = $('#my-form');
176
- $form.find('.field') // Only searches within $form
177
- $form.find('input').val() // Get value within scope
130
+ $form.find('.field') // Scoped selection (efficient)
178
131
  ```
179
132
 
180
133
  ### DOM Manipulation
181
134
 
182
135
  ```javascript
183
136
  // Classes
184
- $el.addClass('active')
185
- $el.removeClass('loading')
186
- $el.toggleClass('visible')
137
+ $el.addClass('active') / $el.removeClass('loading') / $el.toggleClass('visible')
187
138
  $el.hasClass('hidden') // Returns boolean
188
139
 
189
140
  // Content
190
- $el.text('Plain text') // Set text (escapes HTML)
191
- $el.html('<strong>HTML</strong>') // Set HTML
192
- $el.val() // Get form value
193
- $el.val('new value') // Set form value
194
-
195
- // Attributes and Data
196
- $el.attr('href') // Get attribute
197
- $el.attr('href', '/new-url') // Set attribute
198
- $el.prop('checked', true) // Set property (for checkboxes, disabled, etc.)
199
- $el.data('user-id') // Get data-* attribute (cached, parsed)
141
+ $el.text('Plain text') // Safe escapes HTML
142
+ $el.html('<strong>HTML</strong>')
143
+ $el.val() / $el.val('new value')
144
+
145
+ // Attributes / Data
146
+ $el.attr('href') / $el.attr('href', '/new')
147
+ $el.prop('checked', true) // For checkboxes, disabled, etc.
148
+ $el.data('user-id') // Parsed, cached data-* value
200
149
  $el.removeAttr('disabled')
201
150
 
202
- // DOM insertion
203
- $container.append($newElement) // Add inside, at end
204
- $container.prepend($newElement) // Add inside, at start
205
- $el.after($sibling) // Add outside, after
206
- $el.before($sibling) // Add outside, before
207
- $el.wrap('<div class="wrapper"></div>')
208
- $el.remove() // Remove from DOM
209
- $el.empty() // Remove children
210
- $el.clone() // Deep clone
211
-
212
- // CSS and Display
213
- $el.css('color', 'red') // Set single property
214
- $el.css({ color: 'red', fontSize: '14px' }) // Set multiple
215
- $el.show() // display: previous value
216
- $el.hide() // display: none
217
- $el.toggle() // Toggle visibility
151
+ // Insertion
152
+ $container.append($el) / $container.prepend($el)
153
+ $el.after($sibling) / $el.before($sibling)
154
+ $el.remove() / $el.empty() / $el.clone()
155
+
156
+ // CSS / Visibility
157
+ $el.css('color', 'red') / $el.css({ color: 'red', fontSize: '14px' })
158
+ $el.show() / $el.hide() / $el.toggle()
218
159
  ```
219
160
 
220
161
  ### Events
221
162
 
222
163
  ```javascript
223
- // Binding
224
- $el.on('click', handler) // Direct bind
225
- $container.on('click', '.child', handler) // Delegated (dynamic content!)
226
- $el.off('click', handler) // Unbind specific
227
- $el.off('click') // Unbind all click
228
-
229
- // Common events
230
- $el.on('click', fn)
231
- $el.on('change', fn) // Form elements
232
- $el.on('submit', fn) // Forms
233
- $el.on('keyup', fn)
234
- $el.on('input', fn) // Real-time input tracking
235
- $el.on('focus blur', fn) // Multiple events
236
-
237
- // Event object
238
- $el.on('click', function(e) {
239
- e.preventDefault(); // Stop default behavior
240
- e.stopPropagation(); // Stop bubbling
241
- var $this = $(this); // Cache $(this)
242
- var data = $this.data('id');
243
- });
164
+ $el.on('click', handler) // Direct
165
+ $container.on('click', '.child', handler) // Delegated (dynamic content)
166
+ $el.off('click', handler) / $el.off('click')
244
167
 
245
- // Namespaced events (clean removal)
246
- $el.on('click.myPlugin', handler);
247
- $el.off('.myPlugin'); // Remove all myPlugin events
168
+ // Namespaced (clean removal)
169
+ $el.on('click.myPlugin', handler)
170
+ $el.off('.myPlugin')
248
171
 
249
- // One-time events
250
- $el.one('click', handler); // Fires once, then auto-unbinds
172
+ $el.one('click', handler) // Fires once, auto-unbinds
251
173
  ```
252
174
 
253
- ### AJAX
175
+ ### AJAX (WordPress)
254
176
 
255
177
  ```javascript
256
- // Standard WordPress AJAX
257
178
  $.ajax({
258
- url: myVars.ajaxUrl, // wp_localize_script value
179
+ url: myVars.ajaxUrl,
259
180
  type: 'POST',
260
- data: {
261
- action: 'my_action', // WordPress action hook
262
- nonce: myVars.nonce, // Security token
263
- id: itemId
264
- },
265
- success: function(response) {
266
- if (response.success) {
267
- // response.data contains the payload
268
- }
269
- },
270
- error: function(xhr, status, error) {
271
- console.error('AJAX failed:', error);
272
- }
273
- });
274
-
275
- // Shorthand GET
276
- $.get(myVars.restUrl + '/items', function(data) {
277
- renderItems(data);
181
+ data: { action: 'my_action', nonce: myVars.nonce, id: itemId },
182
+ success: function(response) { if (response.success) { /* response.data */ } },
183
+ error: function(xhr, status, error) { console.error('AJAX failed:', error); }
278
184
  });
279
185
 
280
- // Shorthand POST
281
- $.post(myVars.ajaxUrl, { action: 'save_item', data: formData }, function(response) {
282
- handleResponse(response);
283
- });
186
+ // Shorthand
187
+ $.get(myVars.restUrl + '/items', renderItems);
188
+ $.post(myVars.ajaxUrl, { action: 'save_item', data: formData }, handleResponse);
284
189
 
285
- // Promise-style (chainable)
190
+ // Promise-style
286
191
  $.ajax({ url: '/api/data', dataType: 'json' })
287
- .done(function(data) { /* success */ })
288
- .fail(function(xhr) { /* error */ })
289
- .always(function() { /* cleanup */ });
192
+ .done(function(data) { })
193
+ .fail(function(xhr) { })
194
+ .always(function() { });
290
195
  ```
291
196
 
292
197
  ### Utilities
293
198
 
294
199
  ```javascript
295
- // Iteration
296
200
  $.each(array, function(index, value) { });
297
201
  $.each(object, function(key, value) { });
298
- $('.items').each(function(index) {
299
- var $this = $(this); // Cache for performance
300
- });
301
-
302
- // Type checking — prefer native equivalents
303
- Array.isArray(val) // Not $.isArray (deprecated)
304
- typeof val === 'string' // Not $.type (deprecated)
305
- val != null // Not $.isNullOrUndefined
202
+ $('.items').each(function(index) { var $this = $(this); });
306
203
 
307
- // Object merge (deep copy)
308
- var merged = $.extend(true, {}, defaults, options);
204
+ // Prefer native these jQuery equivalents are deprecated:
205
+ Array.isArray(val) // not $.isArray
206
+ typeof val === 'string' // not $.type
309
207
 
310
- // Serialize form data
311
- var data = $form.serialize(); // URL-encoded string
312
- var dataArray = $form.serializeArray(); // Array of {name, value}
208
+ var merged = $.extend(true, {}, defaults, options); // Deep merge
209
+ var data = $form.serialize(); // URL-encoded string
313
210
  ```
314
211
 
315
212
  ## Common Patterns
@@ -317,37 +214,26 @@ var dataArray = $form.serializeArray(); // Array of {name, value}
317
214
  ### Cache jQuery Selections
318
215
 
319
216
  ```javascript
320
- // BAD: Re-querying DOM repeatedly
321
- $('.my-element').addClass('active');
322
- $('.my-element').find('.child').show();
323
- $('.my-element').data('loaded', true);
324
-
325
- // GOOD: Cache the selection
217
+ // GOOD: Cache, then chain
326
218
  var $el = $('.my-element');
327
- $el.addClass('active');
328
- $el.find('.child').show();
329
- $el.data('loaded', true);
219
+ $el.addClass('active').find('.child').show();
330
220
 
331
- // BEST: Chain when possible
221
+ // BEST: Chain with .end()
332
222
  $('.my-element')
333
223
  .addClass('active')
334
224
  .find('.child').show()
335
- .end() // Go back to .my-element
225
+ .end()
336
226
  .data('loaded', true);
337
227
  ```
338
228
 
339
229
  ### Delegated Events for Dynamic Content
340
230
 
341
231
  ```javascript
342
- // BAD: Won't work for dynamically added elements
232
+ // BAD: Won't fire on dynamically added elements
343
233
  $('.delete-btn').on('click', handleDelete);
344
234
 
345
- // GOOD: Delegated — works for current AND future elements
235
+ // GOOD: Fires for current AND future elements
346
236
  $('.item-list').on('click', '.delete-btn', handleDelete);
347
-
348
- // Use delegation when:
349
- // - Elements are added/removed dynamically (AJAX, repeaters)
350
- // - Many identical handlers (performance — one handler vs hundreds)
351
237
  ```
352
238
 
353
239
  ### UI State Management
@@ -356,58 +242,46 @@ $('.item-list').on('click', '.delete-btn', handleDelete);
356
242
  (function($) {
357
243
  'use strict';
358
244
 
359
- // Pure: Compute next state
360
- function getToggleState($el) {
361
- return !$el.hasClass('is-open');
362
- }
245
+ function getToggleState($el) { return !$el.hasClass('is-open'); }
363
246
 
364
- // Impure: Apply state to DOM
365
247
  function applyToggleState($trigger, $target, isOpen) {
366
248
  $trigger.attr('aria-expanded', isOpen);
367
249
  $target.toggleClass('is-open', isOpen);
368
- if (isOpen) {
369
- $target.slideDown(200);
370
- } else {
371
- $target.slideUp(200);
372
- }
250
+ isOpen ? $target.slideDown(200) : $target.slideUp(200);
373
251
  }
374
252
 
375
- // Wire up
376
253
  $('.accordion').on('click', '.accordion-trigger', function(e) {
377
254
  e.preventDefault();
378
255
  var $trigger = $(this);
379
- var $target = $($trigger.data('target'));
380
- applyToggleState($trigger, $target, getToggleState($target));
256
+ applyToggleState($trigger, $($trigger.data('target')), getToggleState($($trigger.data('target'))));
381
257
  });
382
258
  })(jQuery);
383
259
  ```
384
260
 
385
261
  ## Anti-Patterns
386
262
 
387
- | Anti-Pattern | Problem | Fix |
388
- |---|---|---|
389
- | Vanilla JS when jQuery is loaded | Verbose, inconsistent | Use jQuery for DOM work |
390
- | `document.querySelectorAll` + `forEach` in WP | Ignores available jQuery | `$('.selector').each()` |
391
- | Mixing jQuery and vanilla in same file | Inconsistent, confusing | Pick one per file |
392
- | Not caching `$(this)` in loops | Re-wraps DOM element each time | `var $this = $(this)` |
393
- | `$('.selector')` inside loops | Re-queries DOM each iteration | Cache outside loop |
394
- | Direct binding on dynamic elements | Handlers lost on DOM change | Use delegated `.on()` |
395
- | Creating custom `pipe()` / `compose()` | Over-engineering | jQuery chaining IS composition |
396
-
397
- ## Integration
398
-
399
- - **js-fp**: Core FP principles (purity, immutability, testing). jQuery DOM code follows these — pure logic extracted, side effects in DOM wrapper.
400
- - **js-fp-wordpress**: WordPress-specific integration (GF hooks, ACF hooks, admin JS). References this skill for jQuery patterns.
401
- - **ima-bootstrap**: Bootstrap 5 JS components. jQuery available but BS5 doesn't require it — use for custom enhancements.
402
- - **Context7**: For deep jQuery API lookups use library ID `/jquery/jquery`.
263
+ | Anti-Pattern | Fix |
264
+ |---|---|
265
+ | Vanilla `querySelectorAll` + `forEach` in WP | `$('.selector').each()` |
266
+ | Mixing jQuery and vanilla in same file | Pick one per file |
267
+ | Not caching `$(this)` in loops | `var $this = $(this)` |
268
+ | `$('.selector')` inside loops | Cache outside loop |
269
+ | Direct binding on dynamic elements | Use delegated `.on()` |
270
+ | Custom `pipe()` / `compose()` | jQuery chaining IS composition |
403
271
 
404
272
  ## WordPress Coding Standards
405
273
 
406
- Per WordPress JS coding standards:
407
- - Use tabs for indentation
408
- - IIFE wrapper with `jQuery` passed as `$`
409
- - `'use strict'` inside the IIFE
410
- - Spaces inside parentheses: `if ( condition )` not `if (condition)`
411
- - `var` declarations at top of scope (unless using build tools with ES6+)
274
+ - Tabs for indentation
275
+ - IIFE with `jQuery` passed as `$`
276
+ - `'use strict'` inside IIFE
277
+ - Spaces inside parens: `if ( condition )`
278
+ - `var` at top of scope (unless using ES6+ build tools)
279
+
280
+ ## Integration
412
281
 
413
- See [WordPress JavaScript Coding Standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/javascript/) for full reference.
282
+ | Skill | Relationship |
283
+ |-------|-------------|
284
+ | `js-fp` | Core FP principles — pure logic extracted, side effects in DOM wrapper |
285
+ | `js-fp-wordpress` | WordPress-specific (GF hooks, ACF, admin JS) — references this skill |
286
+ | `ima-bootstrap` | Bootstrap 5 JS — jQuery available for custom enhancements |
287
+ | Context7 | Deep jQuery API lookups: library ID `/jquery/jquery` |