@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,264 @@
1
+ ---
2
+ description: PHPUnit testing standards, conventions, and patterns for Silver Assist WordPress plugins using WP_UnitTestCase
3
+ name: Testing Standards
4
+ applyTo: "tests/**/*.php"
5
+ ---
6
+
7
+ # PHPUnit Testing Standards — Silver Assist Plugins
8
+
9
+ **Applies to**: All test files in Silver Assist WordPress plugins
10
+
11
+ ---
12
+
13
+ ## Must-Follow Rules
14
+
15
+ ### 1. Base Class: All Tests Extend TestCase (WP_UnitTestCase)
16
+
17
+ ```php
18
+ use SilverAssist\PluginName\Tests\Helpers\TestCase;
19
+
20
+ class MyTest extends TestCase {
21
+ // Tests here.
22
+ }
23
+ ```
24
+
25
+ ### 2. Lifecycle Methods Use WordPress snake_case
26
+
27
+ Since TestCase extends WP_UnitTestCase, use WordPress-style snake_case:
28
+
29
+ ```php
30
+ // ✅ CORRECT — WordPress snake_case (WP_UnitTestCase).
31
+ public function set_up(): void
32
+ public function tear_down(): void
33
+ public static function set_up_before_class(): void
34
+ public static function tear_down_after_class(): void
35
+
36
+ // ❌ WRONG — PHPUnit camelCase (don't use with WP_UnitTestCase).
37
+ public function setUp(): void
38
+ public function tearDown(): void
39
+ ```
40
+
41
+ ### 3. Test Method Naming — camelCase
42
+
43
+ ```php
44
+ // ✅ CORRECT — Descriptive camelCase test names.
45
+ public function testStartRequestCreatesLogEntry(): void
46
+ public function testSearchMatchesSenderName(): void
47
+
48
+ // ❌ WRONG — Vague names.
49
+ public function testMethod(): void
50
+ public function testItWorks(): void
51
+ ```
52
+
53
+ ### 4. Database Queries: Use %i for Table Names
54
+
55
+ ```php
56
+ // ✅ CORRECT
57
+ $wpdb->prepare( 'SELECT * FROM %i WHERE id = %d', $table_name, $id );
58
+
59
+ // ❌ WRONG — Variable interpolation.
60
+ $wpdb->prepare( "SELECT * FROM {$table_name} WHERE id = %d", $id );
61
+ ```
62
+
63
+ ### 5. Skip When Dependencies Missing
64
+
65
+ ```php
66
+ public function set_up(): void {
67
+ parent::set_up();
68
+
69
+ if ( ! class_exists( 'WPCF7_ContactForm' ) ) {
70
+ $this->markTestSkipped( 'Contact Form 7 not loaded' );
71
+ return; // IMPORTANT: always return after skip.
72
+ }
73
+ }
74
+ ```
75
+
76
+ ### 6. Always Clean Up Resources
77
+
78
+ ```php
79
+ public function tear_down(): void {
80
+ $this->service = null;
81
+ parent::tear_down();
82
+ }
83
+ ```
84
+
85
+ ### 7. Use PHPDoc Group Annotations
86
+
87
+ ```php
88
+ /**
89
+ * Tests for ClassName.
90
+ *
91
+ * @group unit
92
+ * @group logging
93
+ * @covers \SilverAssist\PluginName\Service\ClassName
94
+ */
95
+ class ClassNameTest extends TestCase {
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ## Test Structure Template
102
+
103
+ ```php
104
+ <?php
105
+ /**
106
+ * Tests for ClassName.
107
+ *
108
+ * @package SilverAssist\PluginName\Tests\Unit
109
+ */
110
+
111
+ namespace SilverAssist\PluginName\Tests\Unit\Service\Category;
112
+
113
+ use SilverAssist\PluginName\Tests\Helpers\TestCase;
114
+ use SilverAssist\PluginName\Service\Category\ClassName;
115
+
116
+ /**
117
+ * ClassName test case.
118
+ *
119
+ * @group unit
120
+ * @group service
121
+ * @covers \SilverAssist\PluginName\Service\Category\ClassName
122
+ */
123
+ class ClassNameTest extends TestCase {
124
+
125
+ /**
126
+ * Instance under test.
127
+ *
128
+ * @var ClassName|null
129
+ */
130
+ private ?ClassName $instance = null;
131
+
132
+ /**
133
+ * Set up test fixtures.
134
+ */
135
+ public function set_up(): void {
136
+ parent::set_up();
137
+ $this->instance = new ClassName();
138
+ }
139
+
140
+ /**
141
+ * Tear down test fixtures.
142
+ */
143
+ public function tear_down(): void {
144
+ $this->instance = null;
145
+ parent::tear_down();
146
+ }
147
+
148
+ /**
149
+ * Test that method returns expected value.
150
+ */
151
+ public function testMethodReturnsExpectedValue(): void {
152
+ $result = $this->instance->method( 'input' );
153
+ $this->assertSame( 'expected', $result );
154
+ }
155
+ }
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Data Provider Pattern
161
+
162
+ ```php
163
+ /**
164
+ * Data provider for search tests.
165
+ *
166
+ * @return array<string, array{item: array<string, mixed>, search: string, expected: bool}>
167
+ */
168
+ public static function searchDataProvider(): array {
169
+ return [
170
+ 'exact match' => [
171
+ 'item' => [ 'name' => 'John Doe' ],
172
+ 'search' => 'john',
173
+ 'expected' => true,
174
+ ],
175
+ 'no match' => [
176
+ 'item' => [ 'name' => 'John Doe' ],
177
+ 'search' => 'jane',
178
+ 'expected' => false,
179
+ ],
180
+ ];
181
+ }
182
+
183
+ /**
184
+ * @dataProvider searchDataProvider
185
+ * @param array<string, mixed> $item Item to search.
186
+ * @param string $search Search term.
187
+ * @param bool $expected Expected result.
188
+ */
189
+ public function testSearchMatching( array $item, string $search, bool $expected ): void {
190
+ $result = $this->instance->matches_search( $item, $search );
191
+ $this->assertSame( $expected, $result );
192
+ }
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Assertion Best Practices
198
+
199
+ ```php
200
+ // ✅ Use specific assertions.
201
+ $this->assertSame( 'expected', $actual ); // Strict type + value.
202
+ $this->assertEquals( 5, $count ); // Value equality.
203
+ $this->assertTrue( $result );
204
+ $this->assertNull( $value );
205
+ $this->assertCount( 3, $array );
206
+ $this->assertArrayHasKey( 'key', $array );
207
+ $this->assertInstanceOf( ClassName::class, $obj );
208
+
209
+ // ❌ Avoid generic assertions.
210
+ $this->assertTrue( $result === 'expected' ); // Use assertSame.
211
+ $this->assertTrue( count( $array ) === 3 ); // Use assertCount.
212
+ ```
213
+
214
+ ### Add Assertion Messages
215
+
216
+ ```php
217
+ $this->assertSame(
218
+ 3,
219
+ $result['total'],
220
+ 'Should count 3 total errors (700, 701, 703)'
221
+ );
222
+ ```
223
+
224
+ ---
225
+
226
+ ## WordPress Factory Pattern
227
+
228
+ ```php
229
+ public function testWithFactory(): void {
230
+ $user_id = $this->factory->user->create( [
231
+ 'role' => 'administrator',
232
+ ] );
233
+
234
+ $post_id = $this->factory->post->create( [
235
+ 'post_author' => $user_id,
236
+ 'post_title' => 'Test Post',
237
+ ] );
238
+
239
+ wp_set_current_user( $user_id );
240
+ $this->assertTrue( current_user_can( 'manage_options' ) );
241
+ }
242
+ ```
243
+
244
+ ---
245
+
246
+ ## Running Tests
247
+
248
+ ```bash
249
+ # Run all tests.
250
+ vendor/bin/phpunit
251
+
252
+ # Run specific test file.
253
+ vendor/bin/phpunit tests/Unit/Service/ClassNameTest.php
254
+
255
+ # Run specific test method.
256
+ vendor/bin/phpunit --filter testMethodName
257
+
258
+ # Run tests by group.
259
+ vendor/bin/phpunit --group unit
260
+ vendor/bin/phpunit --group integration
261
+
262
+ # Run with coverage.
263
+ vendor/bin/phpunit --coverage-text
264
+ ```
@@ -0,0 +1,103 @@
1
+ ---
2
+ applyTo: "**/*.test.{ts,tsx}"
3
+ ---
4
+ # Testing Standards
5
+
6
+ ## Test File Location
7
+
8
+ Place tests in `__tests__/` folders next to the code:
9
+
10
+ ```
11
+ components/
12
+ └── button/
13
+ ├── index.tsx
14
+ └── __tests__/
15
+ └── button.test.tsx
16
+ ```
17
+
18
+ ## Test Structure
19
+
20
+ Use `describe` and `it` blocks:
21
+
22
+ ```typescript
23
+ import { render, screen } from '@testing-library/react';
24
+ import userEvent from '@testing-library/user-event';
25
+ import { Button } from '../index';
26
+
27
+ describe('Button', () => {
28
+ const defaultProps = {
29
+ onClick: jest.fn(),
30
+ children: 'Click me',
31
+ };
32
+
33
+ beforeEach(() => {
34
+ jest.clearAllMocks();
35
+ });
36
+
37
+ describe('rendering', () => {
38
+ it('renders children correctly', () => {
39
+ render(<Button {...defaultProps} />);
40
+ expect(screen.getByText('Click me')).toBeInTheDocument();
41
+ });
42
+ });
43
+
44
+ describe('behavior', () => {
45
+ it('calls onClick when clicked', async () => {
46
+ const user = userEvent.setup();
47
+ render(<Button {...defaultProps} />);
48
+
49
+ await user.click(screen.getByRole('button'));
50
+
51
+ expect(defaultProps.onClick).toHaveBeenCalledTimes(1);
52
+ });
53
+ });
54
+ });
55
+ ```
56
+
57
+ ## Test Categories
58
+
59
+ Include tests for:
60
+
61
+ ### Rendering
62
+ - Component renders without crashing
63
+ - All expected elements are present
64
+ - Conditional rendering works
65
+
66
+ ### Props
67
+ - Default props work correctly
68
+ - Custom props are applied
69
+ - Required props are validated
70
+
71
+ ### User Interactions
72
+ - Click handlers work
73
+ - Form inputs update
74
+ - Keyboard navigation
75
+
76
+ ### Edge Cases
77
+ - Empty data handled
78
+ - Null/undefined handled
79
+ - Error states displayed
80
+
81
+ ## Mocking
82
+
83
+ ```typescript
84
+ // Mock a module
85
+ jest.mock('@/lib/api', () => ({
86
+ fetchData: jest.fn(),
87
+ }));
88
+
89
+ // Mock a hook
90
+ jest.mock('@/hooks/use-user', () => ({
91
+ useUser: () => ({ user: { name: 'Test' }, isLoading: false }),
92
+ }));
93
+ ```
94
+
95
+ ## Assertions
96
+
97
+ ```typescript
98
+ // Prefer specific assertions
99
+ expect(screen.getByRole('button')).toBeInTheDocument();
100
+ expect(screen.getByText('Hello')).toBeVisible();
101
+ expect(element).toHaveClass('active');
102
+ expect(handler).toHaveBeenCalledWith(expectedArgs);
103
+ ```
@@ -0,0 +1,106 @@
1
+ ---
2
+ applyTo: "**/*.{ts,tsx}"
3
+ ---
4
+ # TypeScript Code Style Standards
5
+
6
+ ## Export Rules
7
+
8
+ **Components** use `export default`, everything else uses **named exports**:
9
+
10
+ ```typescript
11
+ // ✅ Components: default export
12
+ // components/auth/login-form/index.tsx
13
+ export default function LoginForm() { }
14
+
15
+ // ✅ Everything else: named exports
16
+ // lib/utils.ts
17
+ export function formatDate(date: Date): string { }
18
+ export function validateEmail(email: string): boolean { }
19
+
20
+ // types/user.ts
21
+ export interface User { }
22
+ export type UserRole = "admin" | "user";
23
+
24
+ // actions/auth/login.ts
25
+ export async function login(formData: FormData) { }
26
+
27
+ // hooks/use-form.ts
28
+ export function useForm() { }
29
+ ```
30
+
31
+ ## Avoid Nested Ternaries
32
+ **❌ NEVER use nested ternary operators** - they reduce readability significantly.
33
+
34
+ ```typescript
35
+ // ❌ INCORRECT: Nested ternaries
36
+ const status = isLoading ? 'loading' : hasError ? 'error' : 'success';
37
+
38
+ // ✅ CORRECT: Use if/else or early returns
39
+ function getStatus(isLoading: boolean, hasError: boolean): string {
40
+ if (isLoading) return 'loading';
41
+ if (hasError) return 'error';
42
+ return 'success';
43
+ }
44
+
45
+ // ✅ CORRECT: Simple ternary is OK (no nesting)
46
+ const greeting = isLoggedIn ? 'Welcome back!' : 'Please log in';
47
+ ```
48
+
49
+ ## Clarity Over Brevity
50
+
51
+ **✅ PREFER explicit, readable code** over compact one-liners.
52
+
53
+ ```typescript
54
+ // ❌ INCORRECT: Too compact, hard to understand
55
+ const result = data?.items?.filter(i => i.active).map(i => i.id).join(',') || '';
56
+
57
+ // ✅ CORRECT: Clear and maintainable
58
+ const activeItems = data?.items?.filter(item => item.active) ?? [];
59
+ const itemIds = activeItems.map(item => item.id);
60
+ const result = itemIds.join(',');
61
+ ```
62
+
63
+ ## Object Destructuring
64
+
65
+ **✅ ALWAYS use destructuring** for function parameters and object properties.
66
+
67
+ ```typescript
68
+ // ✅ CORRECT: Destructure at the start
69
+ function processData(data: DataType) {
70
+ const { name, email, phone, address } = data;
71
+ console.log(email);
72
+ sendEmail(email);
73
+ }
74
+ ```
75
+
76
+ ## No `any` Type
77
+
78
+ **❌ NEVER use `any`** - always define explicit types.
79
+
80
+ ```typescript
81
+ // ❌ INCORRECT
82
+ function processData(data: any) { ... }
83
+
84
+ // ✅ CORRECT
85
+ interface DataType {
86
+ name: string;
87
+ email: string;
88
+ }
89
+ function processData(data: DataType) { ... }
90
+ ```
91
+
92
+ ## JSDoc Comments
93
+
94
+ **✅ ALWAYS add JSDoc** to public functions.
95
+
96
+ ```typescript
97
+ /**
98
+ * Formats a date for display.
99
+ * @param date - The date to format
100
+ * @param locale - The locale (default: 'en-US')
101
+ * @returns Formatted date string
102
+ */
103
+ export function formatDate(date: Date, locale = 'en-US'): string {
104
+ return date.toLocaleDateString(locale);
105
+ }
106
+ ```
@@ -0,0 +1,238 @@
1
+ ---
2
+ description: WordPress plugin architecture standards for Silver Assist plugins including LoadableInterface, PSR-4, MVC pattern, singleton, and SilverAssist packages
3
+ name: WordPress Plugin Architecture
4
+ applyTo: "**/*.php"
5
+ ---
6
+
7
+ # WordPress Plugin Architecture — Silver Assist Plugins
8
+
9
+ ---
10
+
11
+ ## Core Standards
12
+
13
+ | Attribute | Value |
14
+ |-----------|-------|
15
+ | PHP | 8.2+ |
16
+ | WordPress | 6.5+ |
17
+ | Autoloading | PSR-4 (PascalCase files and directories) |
18
+ | Standards | PHPCS (WordPress-Extra), PHPStan Level 8 |
19
+
20
+ ---
21
+
22
+ ## PSR-4 Autoloading
23
+
24
+ Namespace `SilverAssist\PluginName\` maps to `includes/`:
25
+
26
+ - PHP classes: `PascalCase.php` matching class name exactly
27
+ - Directories: `PascalCase/` (e.g., `Core/`, `Admin/`, `Service/`)
28
+ - Assets: `kebab-case.css`, `kebab-case.js`
29
+
30
+ ---
31
+
32
+ ## LoadableInterface Priority System
33
+
34
+ All components implement `LoadableInterface` with three methods:
35
+
36
+ ```php
37
+ interface LoadableInterface {
38
+ public function init(): void;
39
+ public function get_priority(): int;
40
+ public function should_load(): bool;
41
+ }
42
+ ```
43
+
44
+ Priority values:
45
+ - **10**: Core components (Plugin, Activator, critical services)
46
+ - **20**: Services (business logic, API clients)
47
+ - **30**: Admin components (controllers, settings pages)
48
+ - **40**: Utils & Assets (helpers, loggers)
49
+
50
+ ### Singleton Pattern
51
+
52
+ ```php
53
+ class ServiceName implements LoadableInterface {
54
+ private static ?self $instance = null;
55
+
56
+ public static function instance(): self {
57
+ if ( null === self::$instance ) {
58
+ self::$instance = new self();
59
+ }
60
+ return self::$instance;
61
+ }
62
+
63
+ private function __construct() {}
64
+
65
+ public function init(): void {
66
+ // Register hooks.
67
+ }
68
+
69
+ public function get_priority(): int {
70
+ return 20;
71
+ }
72
+
73
+ public function should_load(): bool {
74
+ return true;
75
+ }
76
+ }
77
+ ```
78
+
79
+ ### Component Loading (Plugin.php)
80
+
81
+ ```php
82
+ private function get_components(): array {
83
+ return [
84
+ // Core — Priority 10.
85
+ \SilverAssist\PluginName\Core\Activator::class,
86
+ // Services — Priority 20.
87
+ \SilverAssist\PluginName\Service\ServiceName::class,
88
+ // Controllers — Priority 30.
89
+ \SilverAssist\PluginName\Controller\Admin\PageController::class,
90
+ // Utils — Priority 40.
91
+ ];
92
+ }
93
+
94
+ private function load_components(): void {
95
+ $components = [];
96
+ foreach ( $this->get_components() as $class ) {
97
+ if ( method_exists( $class, 'instance' ) ) {
98
+ $instance = $class::instance();
99
+ if ( $instance->should_load() ) {
100
+ $components[] = $instance;
101
+ }
102
+ }
103
+ }
104
+ usort( $components, fn( $a, $b ) => $a->get_priority() <=> $b->get_priority() );
105
+ foreach ( $components as $component ) {
106
+ $component->init();
107
+ }
108
+ }
109
+ ```
110
+
111
+ ---
112
+
113
+ ## MVC Flow Pattern
114
+
115
+ ```
116
+ User Request → Controller → Service → Repository/WordPress API
117
+
118
+ View::render($data) ← Static call with prepared data
119
+ ```
120
+
121
+ | Layer | Responsibility | Can Depend On |
122
+ |-------|---------------|---------------|
123
+ | **Controller** | Handle requests, prepare data, coordinate | Services, Views |
124
+ | **Service** | Business logic, data processing | Other Services, Repositories |
125
+ | **View** | Render HTML (static class) | Only data passed as parameters |
126
+ | **Model** | Data structures | Nothing |
127
+
128
+ ### CRITICAL: Views MUST NOT Instantiate Services
129
+
130
+ ```php
131
+ // ✅ CORRECT — Controller prepares data, View receives it.
132
+ class PageController {
133
+ public function render_page(): void {
134
+ $data = $this->service->get_data();
135
+ PageView::render( $data );
136
+ }
137
+ }
138
+
139
+ class PageView {
140
+ public static function render( array $data ): void {
141
+ // Only use data passed as parameters.
142
+ }
143
+ }
144
+
145
+ // ❌ WRONG — View instantiates Service.
146
+ class PageView {
147
+ public static function render(): void {
148
+ $service = ServiceName::instance(); // ❌ Never do this.
149
+ $data = $service->get_data();
150
+ }
151
+ }
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Directory Structure
157
+
158
+ ```
159
+ plugin-name/
160
+ ├── plugin-name.php # Main plugin file
161
+ ├── composer.json
162
+ ├── phpcs.xml / phpstan.neon / phpunit.xml.dist
163
+ ├── includes/ # PSR-4 classes
164
+ │ ├── Core/ # Priority 10
165
+ │ │ ├── Plugin.php
166
+ │ │ ├── Activator.php
167
+ │ │ └── Interfaces/LoadableInterface.php
168
+ │ ├── Service/ # Priority 20
169
+ │ ├── Controller/ # Priority 30
170
+ │ ├── View/ # Static HTML rendering
171
+ │ ├── Model/ # Domain models
172
+ │ ├── Repository/ # Data access
173
+ │ ├── Infrastructure/ # WordPress integrations
174
+ │ └── Utils/ # Priority 40
175
+ ├── assets/ (css/, js/)
176
+ ├── languages/
177
+ ├── scripts/
178
+ ├── tests/ (Unit/, Integration/, Helpers/)
179
+ └── .github/ (workflows/, copilot-instructions.md)
180
+ ```
181
+
182
+ ---
183
+
184
+ ## SilverAssist Packages
185
+
186
+ ### wp-github-updater
187
+
188
+ Enables automatic updates from GitHub releases:
189
+
190
+ ```php
191
+ $config = new \SilverAssist\GitHubUpdater\UpdaterConfig(
192
+ plugin_basename: PLUGIN_BASENAME,
193
+ version: PLUGIN_VERSION,
194
+ github_repo: 'SilverAssist/plugin-slug',
195
+ plugin_file: PLUGIN_FILE
196
+ );
197
+ $config->init();
198
+ ```
199
+
200
+ ### wp-settings-hub
201
+
202
+ Shared admin dashboard and settings infrastructure.
203
+
204
+ ---
205
+
206
+ ## Security Guards
207
+
208
+ ### File Header
209
+
210
+ Every PHP file must start with:
211
+
212
+ ```php
213
+ \defined( 'ABSPATH' ) || exit;
214
+ ```
215
+
216
+ ### Composer Autoloader Validation
217
+
218
+ Main plugin file validates autoloader path to prevent path traversal:
219
+
220
+ ```php
221
+ $autoload_path = PLUGIN_PATH . 'vendor/autoload.php';
222
+ $real_autoload = realpath( $autoload_path );
223
+ $real_plugin = realpath( PLUGIN_PATH );
224
+
225
+ if ( $real_autoload && $real_plugin && 0 === strpos( $real_autoload, $real_plugin ) ) {
226
+ require_once $real_autoload;
227
+ }
228
+ ```
229
+
230
+ ### Plugin Header — Update URI
231
+
232
+ **CRITICAL**: Must include `Update URI` header to prevent WordPress.org update conflicts:
233
+
234
+ ```php
235
+ /**
236
+ * Update URI: https://github.com/SilverAssist/plugin-slug
237
+ */
238
+ ```