@silverassist/agents-toolkit 2.0.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 (51) hide show
  1. package/LICENSE +135 -0
  2. package/README.md +388 -0
  3. package/bin/cli.js +940 -0
  4. package/package.json +52 -0
  5. package/src/index.js +58 -0
  6. package/templates/agents/AGENTS.codex.md +195 -0
  7. package/templates/agents/AGENTS.md +195 -0
  8. package/templates/agents/CLAUDE.md +213 -0
  9. package/templates/agents/copilot-instructions.md +83 -0
  10. package/templates/shared/instructions/css-styling.instructions.md +142 -0
  11. package/templates/shared/instructions/documentation-language.instructions.md +216 -0
  12. package/templates/shared/instructions/github-workflow.instructions.md +245 -0
  13. package/templates/shared/instructions/php-standards.instructions.md +293 -0
  14. package/templates/shared/instructions/react-components.instructions.md +196 -0
  15. package/templates/shared/instructions/server-actions.instructions.md +429 -0
  16. package/templates/shared/instructions/testing-standards.instructions.md +264 -0
  17. package/templates/shared/instructions/tests.instructions.md +103 -0
  18. package/templates/shared/instructions/typescript.instructions.md +106 -0
  19. package/templates/shared/instructions/wordpress-plugin-architecture.instructions.md +238 -0
  20. package/templates/shared/prompts/README.md +129 -0
  21. package/templates/shared/prompts/_partials/README.md +57 -0
  22. package/templates/shared/prompts/_partials/documentation.md +203 -0
  23. package/templates/shared/prompts/_partials/git-operations.md +171 -0
  24. package/templates/shared/prompts/_partials/github-integration.md +100 -0
  25. package/templates/shared/prompts/_partials/jira-integration.md +155 -0
  26. package/templates/shared/prompts/_partials/pr-template.md +216 -0
  27. package/templates/shared/prompts/_partials/validations.md +87 -0
  28. package/templates/shared/prompts/add-tests.prompt.md +151 -0
  29. package/templates/shared/prompts/analyze-github-issue.prompt.md +73 -0
  30. package/templates/shared/prompts/analyze-ticket.prompt.md +63 -0
  31. package/templates/shared/prompts/create-plan.prompt.md +118 -0
  32. package/templates/shared/prompts/create-pr.prompt.md +139 -0
  33. package/templates/shared/prompts/finalize-pr.prompt.md +150 -0
  34. package/templates/shared/prompts/fix-issues.prompt.md +92 -0
  35. package/templates/shared/prompts/new-wp-component.prompt.md +51 -0
  36. package/templates/shared/prompts/new-wp-plugin.prompt.md +46 -0
  37. package/templates/shared/prompts/prepare-pr.prompt.md +123 -0
  38. package/templates/shared/prompts/prepare-release.prompt.md +74 -0
  39. package/templates/shared/prompts/quality-check.prompt.md +45 -0
  40. package/templates/shared/prompts/review-code.prompt.md +81 -0
  41. package/templates/shared/prompts/work-github-issue.prompt.md +96 -0
  42. package/templates/shared/prompts/work-ticket.prompt.md +98 -0
  43. package/templates/shared/skills/README.md +53 -0
  44. package/templates/shared/skills/component-architecture/SKILL.md +321 -0
  45. package/templates/shared/skills/create-component/SKILL.md +714 -0
  46. package/templates/shared/skills/domain-driven-design/SKILL.md +277 -0
  47. package/templates/shared/skills/plugin-creation/SKILL.md +1094 -0
  48. package/templates/shared/skills/quality-checks/SKILL.md +493 -0
  49. package/templates/shared/skills/release-management/SKILL.md +649 -0
  50. package/templates/shared/skills/testing/SKILL.md +682 -0
  51. package/templates/shared/skills/testing-patterns/SKILL.md +243 -0
@@ -0,0 +1,714 @@
1
+ ---
2
+ name: create-component
3
+ description: Create new components in Silver Assist WordPress plugins following the LoadableInterface pattern. Covers adding services, controllers, views, models, repositories, and infrastructure components with proper registration, PHPDoc, and quality checks. Use when adding a new class or feature to an existing plugin.
4
+ ---
5
+
6
+ # Silver Assist — Create Component
7
+
8
+ This skill covers adding new components to an existing Silver Assist WordPress plugin. It provides templates, registration steps, and quality checks for each component type.
9
+
10
+ ## When to Use
11
+
12
+ - Adding a new Service, Controller, View, Model, or Repository
13
+ - Creating a new admin page or feature
14
+ - Adding infrastructure components (ListTable, Widget, Handler)
15
+ - Registering a new component in `Plugin.php`
16
+ - Understanding which component type to use
17
+
18
+ ## Component Type Decision Guide
19
+
20
+ | Need | Component Type | Priority | LoadableInterface? |
21
+ |------|---------------|----------|-------------------|
22
+ | Business logic, API calls, data processing | **Service** | 20 | Yes |
23
+ | Handle HTTP/admin requests, coordinate Service→View | **Controller** | 30 | Yes |
24
+ | Render HTML output | **View** | N/A | No (static class) |
25
+ | Represent data structures | **Model** | N/A | No (plain object) |
26
+ | Database queries, data access | **Repository** | N/A | No (used by Services) |
27
+ | WordPress integrations (WP_List_Table, widgets) | **Infrastructure** | 30 | Optional |
28
+ | Shared utilities | **Utils** | 40 | Yes |
29
+ | Plugin bootstrap, lifecycle | **Core** | 10 | Yes |
30
+
31
+ ## Directory Structure
32
+
33
+ ```
34
+ includes/
35
+ ├── Core/ # Priority 10 — Bootstrap & lifecycle
36
+ │ ├── Plugin.php # Main plugin bootstrap (singleton)
37
+ │ ├── Activator.php # Activation/deactivation logic
38
+ │ └── Interfaces/
39
+ │ └── LoadableInterface.php
40
+ ├── Service/ # Priority 20 — Business logic
41
+ │ ├── Loader.php # Service loader (optional)
42
+ │ └── Category/ # Group related services
43
+ │ └── ServiceName.php
44
+ ├── Controller/ # Priority 30 — Request handlers
45
+ │ ├── Admin/ # Admin page controllers
46
+ │ └── Frontend/ # Frontend controllers
47
+ ├── View/ # No priority — HTML rendering (static)
48
+ │ ├── Admin/ # Admin views
49
+ │ └── Frontend/ # Frontend views
50
+ ├── Model/ # No priority — Domain models
51
+ ├── Repository/ # No priority — Data access
52
+ ├── Infrastructure/ # Priority 30 — WordPress integrations
53
+ │ ├── ListTable/
54
+ │ └── Widget/
55
+ ├── Config/ # Configuration management
56
+ ├── Exception/ # Custom exceptions
57
+ └── Utils/ # Priority 40 — Utilities
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Templates
63
+
64
+ ### Service Class (Priority 20)
65
+
66
+ Services contain business logic. They implement `LoadableInterface` and use the singleton pattern.
67
+
68
+ ```php
69
+ <?php
70
+ /**
71
+ * ServiceName Service
72
+ *
73
+ * Description of what this service does.
74
+ *
75
+ * @package SilverAssist\PluginName
76
+ * @subpackage Service\Category
77
+ * @since X.Y.Z
78
+ * @version X.Y.Z
79
+ */
80
+
81
+ namespace SilverAssist\PluginName\Service\Category;
82
+
83
+ use SilverAssist\PluginName\Core\Interfaces\LoadableInterface;
84
+
85
+ \defined( 'ABSPATH' ) || exit;
86
+
87
+ /**
88
+ * Class ServiceName
89
+ *
90
+ * Detailed description of the service's responsibility.
91
+ *
92
+ * @since X.Y.Z
93
+ */
94
+ class ServiceName implements LoadableInterface {
95
+
96
+ /**
97
+ * Singleton instance.
98
+ *
99
+ * @var self|null
100
+ */
101
+ private static ?self $instance = null;
102
+
103
+ /**
104
+ * Get singleton instance.
105
+ *
106
+ * @return self
107
+ */
108
+ public static function instance(): self {
109
+ if ( null === self::$instance ) {
110
+ self::$instance = new self();
111
+ }
112
+ return self::$instance;
113
+ }
114
+
115
+ /**
116
+ * Private constructor for singleton.
117
+ */
118
+ private function __construct() {}
119
+
120
+ /**
121
+ * Initialize the service.
122
+ *
123
+ * Register WordPress hooks and filters here.
124
+ *
125
+ * @return void
126
+ */
127
+ public function init(): void {
128
+ // Register hooks here.
129
+ }
130
+
131
+ /**
132
+ * Get loading priority.
133
+ *
134
+ * @return int
135
+ */
136
+ public function get_priority(): int {
137
+ return 20;
138
+ }
139
+
140
+ /**
141
+ * Check if this service should load.
142
+ *
143
+ * @return bool
144
+ */
145
+ public function should_load(): bool {
146
+ return true;
147
+ }
148
+ }
149
+ ```
150
+
151
+ ### Controller Class (Priority 30)
152
+
153
+ Controllers handle HTTP/admin requests. They coordinate between Services (data) and Views (rendering).
154
+
155
+ **CRITICAL**: Controllers prepare ALL data and pass it to Views. Views never instantiate Services.
156
+
157
+ ```php
158
+ <?php
159
+ /**
160
+ * ControllerName Controller
161
+ *
162
+ * Handles requests for feature.
163
+ *
164
+ * @package SilverAssist\PluginName
165
+ * @subpackage Controller\Admin
166
+ * @since X.Y.Z
167
+ * @version X.Y.Z
168
+ */
169
+
170
+ namespace SilverAssist\PluginName\Controller\Admin;
171
+
172
+ use SilverAssist\PluginName\Core\Interfaces\LoadableInterface;
173
+ use SilverAssist\PluginName\Service\Category\ServiceName;
174
+ use SilverAssist\PluginName\View\Admin\ViewName;
175
+
176
+ \defined( 'ABSPATH' ) || exit;
177
+
178
+ /**
179
+ * Class ControllerName
180
+ *
181
+ * Coordinates Service → View for feature.
182
+ *
183
+ * @since X.Y.Z
184
+ */
185
+ class ControllerName implements LoadableInterface {
186
+
187
+ /**
188
+ * Singleton instance.
189
+ *
190
+ * @var self|null
191
+ */
192
+ private static ?self $instance = null;
193
+
194
+ /**
195
+ * Service instance.
196
+ *
197
+ * @var ServiceName
198
+ */
199
+ private ServiceName $service;
200
+
201
+ /**
202
+ * Get singleton instance.
203
+ *
204
+ * @return self
205
+ */
206
+ public static function instance(): self {
207
+ if ( null === self::$instance ) {
208
+ self::$instance = new self();
209
+ }
210
+ return self::$instance;
211
+ }
212
+
213
+ /**
214
+ * Private constructor for singleton.
215
+ */
216
+ private function __construct() {
217
+ $this->service = ServiceName::instance();
218
+ }
219
+
220
+ /**
221
+ * Initialize controller.
222
+ *
223
+ * @return void
224
+ */
225
+ public function init(): void {
226
+ \add_action( 'admin_menu', [ $this, 'register_page' ] );
227
+ }
228
+
229
+ /**
230
+ * Get loading priority.
231
+ *
232
+ * @return int
233
+ */
234
+ public function get_priority(): int {
235
+ return 30;
236
+ }
237
+
238
+ /**
239
+ * Check if should load.
240
+ *
241
+ * @return bool
242
+ */
243
+ public function should_load(): bool {
244
+ return \is_admin();
245
+ }
246
+
247
+ /**
248
+ * Register admin page.
249
+ *
250
+ * @return void
251
+ */
252
+ public function register_page(): void {
253
+ \add_submenu_page(
254
+ 'parent-slug',
255
+ \__( 'Page Title', 'plugin-text-domain' ),
256
+ \__( 'Menu Title', 'plugin-text-domain' ),
257
+ 'manage_options',
258
+ 'page-slug',
259
+ [ $this, 'render_page' ]
260
+ );
261
+ }
262
+
263
+ /**
264
+ * Render page.
265
+ *
266
+ * Controller prepares all data, then delegates rendering to the View.
267
+ *
268
+ * @return void
269
+ */
270
+ public function render_page(): void {
271
+ // ✅ Controller prepares ALL data.
272
+ $data = $this->service->get_data();
273
+
274
+ // ✅ Pass prepared data to static View.
275
+ ViewName::render( $data );
276
+ }
277
+ }
278
+ ```
279
+
280
+ ### View Class (Static — No LoadableInterface)
281
+
282
+ Views are static classes that render HTML. They NEVER instantiate Services or access the database directly.
283
+
284
+ **CRITICAL**: All data must be passed as parameters from the Controller.
285
+
286
+ ```php
287
+ <?php
288
+ /**
289
+ * ViewName View
290
+ *
291
+ * HTML rendering for feature.
292
+ * NOTE: Views receive all data as parameters — never instantiate Services here.
293
+ *
294
+ * @package SilverAssist\PluginName
295
+ * @subpackage View\Admin
296
+ * @since X.Y.Z
297
+ * @version X.Y.Z
298
+ */
299
+
300
+ namespace SilverAssist\PluginName\View\Admin;
301
+
302
+ \defined( 'ABSPATH' ) || exit;
303
+
304
+ /**
305
+ * Class ViewName
306
+ *
307
+ * Renders HTML for feature.
308
+ *
309
+ * @since X.Y.Z
310
+ */
311
+ class ViewName {
312
+
313
+ /**
314
+ * Render the main view.
315
+ *
316
+ * All data must be passed as parameters from the Controller.
317
+ * Views should NEVER instantiate Services directly.
318
+ *
319
+ * @param array<string, mixed> $data Data to render (prepared by Controller).
320
+ * @return void
321
+ */
322
+ public static function render( array $data ): void {
323
+ ?>
324
+ <div class="wrap">
325
+ <h1><?php \esc_html_e( 'Page Title', 'plugin-text-domain' ); ?></h1>
326
+ <?php if ( ! empty( $data ) ) : ?>
327
+ <!-- Render data using proper escaping -->
328
+ <p><?php echo \esc_html( $data['message'] ?? '' ); ?></p>
329
+ <?php endif; ?>
330
+ </div>
331
+ <?php
332
+ }
333
+ }
334
+ ```
335
+
336
+ ### Model Class (No LoadableInterface)
337
+
338
+ Models represent data structures. They are plain PHP objects with typed properties.
339
+
340
+ ```php
341
+ <?php
342
+ /**
343
+ * ModelName Model
344
+ *
345
+ * Represents a data entity.
346
+ *
347
+ * @package SilverAssist\PluginName
348
+ * @subpackage Model
349
+ * @since X.Y.Z
350
+ * @version X.Y.Z
351
+ */
352
+
353
+ namespace SilverAssist\PluginName\Model;
354
+
355
+ \defined( 'ABSPATH' ) || exit;
356
+
357
+ /**
358
+ * Class ModelName
359
+ *
360
+ * Data transfer object for entity.
361
+ *
362
+ * @since X.Y.Z
363
+ */
364
+ class ModelName {
365
+
366
+ /**
367
+ * Constructor.
368
+ *
369
+ * @param int $id Entity ID.
370
+ * @param string $name Entity name.
371
+ * @param string $status Entity status.
372
+ */
373
+ public function __construct(
374
+ public readonly int $id,
375
+ public readonly string $name,
376
+ public readonly string $status = 'active',
377
+ ) {}
378
+
379
+ /**
380
+ * Create from database row.
381
+ *
382
+ * @param object $row Database row object.
383
+ * @return self
384
+ */
385
+ public static function from_row( object $row ): self {
386
+ return new self(
387
+ id: (int) $row->id,
388
+ name: (string) $row->name,
389
+ status: (string) ( $row->status ?? 'active' ),
390
+ );
391
+ }
392
+
393
+ /**
394
+ * Convert to array.
395
+ *
396
+ * @return array<string, mixed>
397
+ */
398
+ public function to_array(): array {
399
+ return [
400
+ 'id' => $this->id,
401
+ 'name' => $this->name,
402
+ 'status' => $this->status,
403
+ ];
404
+ }
405
+ }
406
+ ```
407
+
408
+ ### Repository Class (No LoadableInterface)
409
+
410
+ Repositories handle database queries. They are used by Services.
411
+
412
+ ```php
413
+ <?php
414
+ /**
415
+ * ModelName Repository
416
+ *
417
+ * Data access for entity.
418
+ *
419
+ * @package SilverAssist\PluginName
420
+ * @subpackage Repository
421
+ * @since X.Y.Z
422
+ * @version X.Y.Z
423
+ */
424
+
425
+ namespace SilverAssist\PluginName\Repository;
426
+
427
+ use SilverAssist\PluginName\Model\ModelName;
428
+
429
+ \defined( 'ABSPATH' ) || exit;
430
+
431
+ /**
432
+ * Class ModelNameRepository
433
+ *
434
+ * Handles database operations for entity.
435
+ *
436
+ * @since X.Y.Z
437
+ */
438
+ class ModelNameRepository {
439
+
440
+ /**
441
+ * Table name (without prefix).
442
+ *
443
+ * @var string
444
+ */
445
+ private string $table;
446
+
447
+ /**
448
+ * Constructor.
449
+ */
450
+ public function __construct() {
451
+ global $wpdb;
452
+ $this->table = $wpdb->prefix . 'plugin_prefix_table';
453
+ }
454
+
455
+ /**
456
+ * Find by ID.
457
+ *
458
+ * @param int $id Entity ID.
459
+ * @return ModelName|null
460
+ */
461
+ public function find( int $id ): ?ModelName {
462
+ global $wpdb;
463
+
464
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
465
+ $row = $wpdb->get_row(
466
+ $wpdb->prepare(
467
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
468
+ "SELECT * FROM {$this->table} WHERE id = %d",
469
+ $id
470
+ )
471
+ );
472
+
473
+ if ( ! $row ) {
474
+ return null;
475
+ }
476
+
477
+ return ModelName::from_row( $row );
478
+ }
479
+
480
+ /**
481
+ * Find all with pagination.
482
+ *
483
+ * @param int $per_page Items per page.
484
+ * @param int $page Page number.
485
+ * @return array<int, ModelName>
486
+ */
487
+ public function find_all( int $per_page = 20, int $page = 1 ): array {
488
+ global $wpdb;
489
+
490
+ $offset = ( $page - 1 ) * $per_page;
491
+
492
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
493
+ $rows = $wpdb->get_results(
494
+ $wpdb->prepare(
495
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
496
+ "SELECT * FROM {$this->table} ORDER BY id DESC LIMIT %d OFFSET %d",
497
+ $per_page,
498
+ $offset
499
+ )
500
+ );
501
+
502
+ return array_map(
503
+ fn( object $row ) => ModelName::from_row( $row ),
504
+ $rows
505
+ );
506
+ }
507
+
508
+ /**
509
+ * Save entity.
510
+ *
511
+ * @param ModelName $model Entity to save.
512
+ * @return int|false Inserted ID or false on failure.
513
+ */
514
+ public function save( ModelName $model ): int|false {
515
+ global $wpdb;
516
+
517
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
518
+ $result = $wpdb->insert(
519
+ $this->table,
520
+ $model->to_array(),
521
+ [ '%d', '%s', '%s' ]
522
+ );
523
+
524
+ return $result ? (int) $wpdb->insert_id : false;
525
+ }
526
+
527
+ /**
528
+ * Delete by ID.
529
+ *
530
+ * @param int $id Entity ID.
531
+ * @return bool
532
+ */
533
+ public function delete( int $id ): bool {
534
+ global $wpdb;
535
+
536
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
537
+ $result = $wpdb->delete(
538
+ $this->table,
539
+ [ 'id' => $id ],
540
+ [ '%d' ]
541
+ );
542
+
543
+ return (bool) $result;
544
+ }
545
+ }
546
+ ```
547
+
548
+ ---
549
+
550
+ ## Registration Steps
551
+
552
+ After creating a component class that implements `LoadableInterface`:
553
+
554
+ ### 1. Register in Plugin.php
555
+
556
+ Edit `includes/Core/Plugin.php` — add the class to `get_components()`:
557
+
558
+ ```php
559
+ private function get_components(): array {
560
+ return [
561
+ // Core - Priority 10.
562
+ \SilverAssist\PluginName\Core\Activator::class,
563
+
564
+ // Services - Priority 20.
565
+ \SilverAssist\PluginName\Service\Category\ServiceName::class, // ← NEW
566
+
567
+ // Controllers - Priority 30.
568
+ \SilverAssist\PluginName\Controller\Admin\ControllerName::class, // ← NEW
569
+
570
+ // Utils - Priority 40.
571
+ ];
572
+ }
573
+ ```
574
+
575
+ **Rules:**
576
+ - Group by priority tier with comments.
577
+ - Keep entries alphabetically within each group.
578
+ - Only add classes that implement `LoadableInterface`.
579
+ - Models, Views, Repositories, and Exceptions are NOT registered here.
580
+
581
+ ### 2. Create Matching Test File
582
+
583
+ Test file mirrors the source file path under `tests/Unit/`:
584
+
585
+ | Source | Test |
586
+ |--------|------|
587
+ | `includes/Service/Api/ApiClient.php` | `tests/Unit/Service/Api/ApiClientTest.php` |
588
+ | `includes/Controller/Admin/LogsController.php` | `tests/Unit/Controller/Admin/LogsControllerTest.php` |
589
+ | `includes/Model/LogEntry.php` | `tests/Unit/Model/LogEntryTest.php` |
590
+
591
+ ```php
592
+ <?php
593
+ namespace SilverAssist\PluginName\Tests\Unit\Service\Category;
594
+
595
+ use SilverAssist\PluginName\Tests\Helpers\TestCase;
596
+ use SilverAssist\PluginName\Service\Category\ServiceName;
597
+
598
+ /**
599
+ * Tests for ServiceName.
600
+ *
601
+ * @covers \SilverAssist\PluginName\Service\Category\ServiceName
602
+ */
603
+ class ServiceNameTest extends TestCase {
604
+
605
+ /**
606
+ * Service instance.
607
+ *
608
+ * @var ServiceName
609
+ */
610
+ private ServiceName $service;
611
+
612
+ /**
613
+ * Set up test fixtures.
614
+ *
615
+ * @return void
616
+ */
617
+ public function set_up(): void {
618
+ parent::set_up();
619
+ $this->service = ServiceName::instance();
620
+ }
621
+
622
+ /**
623
+ * Test singleton returns same instance.
624
+ *
625
+ * @return void
626
+ */
627
+ public function testInstanceReturnsSameObject(): void {
628
+ $instance1 = ServiceName::instance();
629
+ $instance2 = ServiceName::instance();
630
+ $this->assertSame( $instance1, $instance2 );
631
+ }
632
+
633
+ /**
634
+ * Test get_priority returns expected value.
635
+ *
636
+ * @return void
637
+ */
638
+ public function testGetPriorityReturnsExpectedValue(): void {
639
+ $this->assertSame( 20, $this->service->get_priority() );
640
+ }
641
+
642
+ /**
643
+ * Test should_load returns true.
644
+ *
645
+ * @return void
646
+ */
647
+ public function testShouldLoadReturnsTrue(): void {
648
+ $this->assertTrue( $this->service->should_load() );
649
+ }
650
+ }
651
+ ```
652
+
653
+ ### 3. Run Quality Checks
654
+
655
+ ```bash
656
+ # Check PHPCS compliance for the new file.
657
+ vendor/bin/phpcs includes/Service/Category/ServiceName.php
658
+
659
+ # Run PHPStan on the new file.
660
+ php -d memory_limit=512M vendor/bin/phpstan analyse includes/Service/Category/ServiceName.php --level=8
661
+
662
+ # Run the matching test.
663
+ vendor/bin/phpunit --filter ServiceNameTest
664
+
665
+ # Full quality check.
666
+ ./scripts/run-quality-checks.sh
667
+ ```
668
+
669
+ ---
670
+
671
+ ## MVC Flow Pattern
672
+
673
+ The correct data flow in Silver Assist plugins is:
674
+
675
+ ```
676
+ User Request → Controller → Service → Repository/WordPress API
677
+
678
+ View::render($data) ← Static call with prepared data
679
+ ```
680
+
681
+ **Rules:**
682
+ 1. **Controller** receives the request, calls Service methods, prepares data, passes to View.
683
+ 2. **Service** contains business logic, calls Repositories or WordPress APIs.
684
+ 3. **View** renders HTML using ONLY the data passed as parameters.
685
+ 4. **Model** is a data structure used to transfer data between layers.
686
+ 5. **Repository** handles database operations, returns Models.
687
+
688
+ **Anti-patterns to avoid:**
689
+ - ❌ View instantiating a Service: `$service = ServiceName::instance();`
690
+ - ❌ View making database queries directly.
691
+ - ❌ Controller containing business logic (should delegate to Service).
692
+ - ❌ Service rendering HTML (should delegate to View via Controller).
693
+
694
+ ---
695
+
696
+ ## Component Checklist
697
+
698
+ Before submitting a PR with a new component:
699
+
700
+ - [ ] Follows namespace convention `SilverAssist\PluginName\`
701
+ - [ ] File path matches PSR-4 autoloading (PascalCase)
702
+ - [ ] Implements `LoadableInterface` (if needs initialization)
703
+ - [ ] Uses singleton pattern with `instance()` method
704
+ - [ ] Has complete PHPDoc with `@package`, `@since`, `@version`
705
+ - [ ] Uses `\` prefix for WordPress functions in namespaced code
706
+ - [ ] Uses `\defined( 'ABSPATH' ) || exit;` security guard
707
+ - [ ] Single quotes for simple strings, double for interpolation
708
+ - [ ] Registered in `Plugin.php::get_components()` (if LoadableInterface)
709
+ - [ ] Matching test file created in `tests/Unit/`
710
+ - [ ] Views do NOT instantiate Services (data passed from Controller)
711
+ - [ ] Controllers prepare all data before passing to Views
712
+ - [ ] Passes `vendor/bin/phpcs`
713
+ - [ ] Passes `vendor/bin/phpstan analyse --level=8`
714
+ - [ ] Test passes with `vendor/bin/phpunit --filter ComponentNameTest`