@silverassist/agents-toolkit 2.3.0 → 2.5.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.
package/src/index.js CHANGED
@@ -3,45 +3,84 @@
3
3
  * @module @silverassist/agents-toolkit
4
4
  */
5
5
 
6
- export const VERSION = "2.3.0";
6
+ export const VERSION = "2.5.0";
7
7
 
8
8
  export const PROMPTS = {
9
9
  workflow: [
10
+ "analyze-github-issue",
10
11
  "analyze-ticket",
12
+ "create-github-pr",
11
13
  "create-plan",
12
- "work-ticket",
13
- "prepare-pr",
14
14
  "create-pr",
15
+ "finalize-github-pr",
15
16
  "finalize-pr",
17
+ "prepare-github-release",
18
+ "prepare-pr",
19
+ "work-github-issue",
20
+ "work-ticket",
21
+ ],
22
+ utility: [
23
+ "add-tests",
24
+ "audit-ai-seo",
25
+ "fix-issues",
26
+ "new-wp-component",
27
+ "new-wp-plugin",
28
+ "quality-check",
29
+ "review-code",
16
30
  ],
17
- utility: ["review-code", "fix-issues", "add-tests"],
18
31
  };
19
32
 
20
33
  export const PARTIALS = [
21
- "validations",
34
+ "documentation",
22
35
  "git-operations",
36
+ "github-integration",
23
37
  "jira-integration",
24
- "documentation",
25
38
  "pr-template",
39
+ "release-node",
40
+ "release-wordpress",
41
+ "validations",
26
42
  ];
27
43
 
28
44
  export const INSTRUCTIONS = [
29
- "typescript",
45
+ "caching",
46
+ "css-styling",
47
+ "documentation-language",
48
+ "github-workflow",
49
+ "php-standards",
30
50
  "react-components",
51
+ "seo-ai-optimization",
31
52
  "server-actions",
53
+ "testing-standards",
32
54
  "tests",
33
- "css-styling",
55
+ "typescript",
56
+ "wordpress-plugin-architecture",
34
57
  ];
35
58
 
36
59
  export const SKILLS = [
60
+ "ai-seo-optimization",
37
61
  "component-architecture",
62
+ "create-component",
38
63
  "domain-driven-design",
64
+ "nextjs-caching",
65
+ "plugin-creation",
66
+ "quality-checks",
67
+ "release-management",
68
+ "testing",
39
69
  "testing-patterns",
40
- "ai-seo-optimization",
41
70
  ];
42
71
 
43
72
  export const HOOKS = ["validate-tsx", "lint-format"];
44
73
 
74
+ // Skills follow the `npx skills` standard: a single canonical copy lives in
75
+ // .agents/skills/ and each agent's skills directory symlinks to it.
76
+ export const SKILLS_LAYOUT = {
77
+ canonicalDir: ".agents/skills",
78
+ agentDirs: {
79
+ claude: ".claude/skills",
80
+ copilot: ".github/skills",
81
+ },
82
+ };
83
+
45
84
  // Claude Code equivalents
46
85
  export const CLAUDE_COMMANDS = [
47
86
  "analyze-ticket",
@@ -58,4 +97,5 @@ export const CLAUDE_COMMANDS = [
58
97
  export const CLAUDE_FILES = {
59
98
  instructions: "CLAUDE.md",
60
99
  commandsDir: ".claude/commands",
100
+ skillsDir: ".claude/skills",
61
101
  };
@@ -13,11 +13,13 @@
13
13
 
14
14
  ```
15
15
  [Instructions]|root:.github/instructions
16
- |css-styling.instructions.md CSS/Tailwind patterns, cn() utility, responsive design
17
- |react-components.instructions.md Component structure, exports, props, early returns
18
- |server-actions.instructions.md → Server action patterns, validation, error handling
19
- |tests.instructions.md Test structure, mocking, assertions
20
- |typescript.instructions.md Type safety, destructuring, JSDoc
16
+ |caching.instructions.md Next.js caching: read-vs-mutation fetch, ISR tiers, CDN invalidation
17
+ |css-styling.instructions.md CSS/Tailwind patterns, cn() utility, responsive design
18
+ |react-components.instructions.md → Component structure, exports, props, early returns
19
+ |seo-ai-optimization.instructions.md Semantic HTML, a11y tree, metadata, JSON-LD for AI Search
20
+ |server-actions.instructions.md Server action patterns, validation, error handling
21
+ |tests.instructions.md → Test structure, mocking, assertions
22
+ |typescript.instructions.md → Type safety, destructuring, JSDoc
21
23
 
22
24
  [Prompts]|root:.github/prompts
23
25
  |add-tests,analyze-ticket,create-plan,create-pr,finalize-pr,fix-issues,prepare-pr,review-code,work-ticket
@@ -133,6 +135,30 @@ import { myFunction } from '@/lib/my-module';
133
135
 
134
136
  ---
135
137
 
138
+ ## 🗄️ Caching Rules (CRITICAL)
139
+
140
+ | Rule | Requirement |
141
+ |------|-------------|
142
+ | **Cache by intent** | Cache reads, never mutations — decide on intent, NOT the HTTP method |
143
+ | **POST reads** | A `POST` read (e.g. filter/`geo-search`) MUST set `next: { revalidate, tags }` to cache |
144
+ | **Mutations** | `submit`/lead/`PUT`/`DELETE` → `cache: "no-store"` (flag with `mutation: true`) |
145
+ | **ISR** | Every cacheable route exports `revalidate`; never on form/personalized routes |
146
+ | **Invalidation** | `/api/revalidate` calls `revalidateTag`/`revalidatePath` AND the CDN invalidation |
147
+
148
+ ```ts
149
+ // ❌ WRONG: leaves POST reads uncached → route turns dynamic (private, no-store)
150
+ next: method === "GET" ? { revalidate, tags } : { revalidate: 0 }
151
+
152
+ // ✅ CORRECT: reads cache regardless of method; mutations opt out
153
+ const isMutation = mutation || method === "PUT" || method === "DELETE";
154
+ ...(isMutation ? { cache: "no-store" } : { next: { revalidate, tags } })
155
+ ```
156
+
157
+ 📄 **Full details:** `.github/instructions/caching.instructions.md`
158
+ 📄 **Project-specific context (if present):** `docs/CACHING.md`
159
+
160
+ ---
161
+
136
162
  ## 📝 Git Conventions
137
163
 
138
164
  | Type | Format |
@@ -190,6 +216,8 @@ Before ANY push to dev/staging/main:
190
216
  | Creating/editing components | `react-components.instructions.md` |
191
217
  | Writing CSS/Tailwind | `css-styling.instructions.md` |
192
218
  | Creating server actions | `server-actions.instructions.md` |
219
+ | Data fetching / `next.config` / routes / ISR / revalidate | `caching.instructions.md` |
220
+ | Pages, metadata, JSON-LD, accessibility/SEO | `seo-ai-optimization.instructions.md` |
193
221
  | Writing tests | `tests.instructions.md` |
194
222
  | TypeScript questions | `typescript.instructions.md` |
195
223
  | **Before pushing/PR** | `tests.instructions.md` + run quality checks |
@@ -1,4 +1,5 @@
1
1
  {
2
+ "version": 1,
2
3
  "hooks": {
3
4
  "PostToolUse": [
4
5
  {
@@ -1,4 +1,5 @@
1
1
  {
2
+ "version": 1,
2
3
  "hooks": {
3
4
  "PostToolUse": [
4
5
  {
@@ -0,0 +1,121 @@
1
+ ---
2
+ applyTo: "**/next.config.*,**/src/proxy.ts,**/src/lib/**/*.ts,**/src/app/**/route.ts,**/src/app/**/page.tsx"
3
+ ---
4
+ # Caching Standards (CRITICAL)
5
+
6
+ These rules are **mandatory** when touching data fetching, route segment configs, the asset/page
7
+ proxy, image config, or the on-demand revalidate route. Caching has several **independent layers**
8
+ (ISR page cache, data-fetch cache, request dedup, asset proxy, edge/CDN) — fixing one does not fix
9
+ the others.
10
+
11
+ > Apply to any Silver Side Next.js (App Router) frontend consuming the CCDS API and/or WordPress
12
+ > (headless GraphQL). If the project keeps a `docs/CACHING.md`, treat it as the extended reference.
13
+
14
+ ## Hard rules
15
+
16
+ 1. **Cache reads, not mutations — decide on intent, NEVER on the HTTP method.**
17
+ Some reads must use `POST` because they take a body (e.g. CCDS `geo-search`). They still have to
18
+ cache like a `GET`. Mutations (`submit-review`, lead submit, and any `PUT`/`DELETE`/`PATCH`) must
19
+ stay uncached with `cache: "no-store"`. Expose a `mutation: true` flag on the client and mark every
20
+ write with it.
21
+ - ❌ **Never** gate caching on `method === "GET"` (e.g. `next: method === "GET" ? {...} : { revalidate: 0 }`).
22
+ That leaves POST reads uncached and forces the whole route into dynamic rendering
23
+ (`cache-control: private, no-store`).
24
+
25
+ 2. **A `POST` IS cacheable cross-request — but only with explicit `next` options.**
26
+ Next.js does not cache `POST` by default. Any read `fetch` to CCDS or the WP GraphQL endpoint —
27
+ GET or POST — must set `next: { revalidate, tags }`. The request body is part of the cache key, so
28
+ different filter bodies cache independently.
29
+
30
+ 3. **Always pair `tags` with a `revalidate` duration.** `next: { tags }` alone either holds stale data
31
+ indefinitely or (Next 16 default) does not cache at runtime at all. Use a `CACHE_DURATIONS`-style
32
+ default (e.g. 24h) so freshness survives a missed webhook.
33
+
34
+ 4. **`React.cache()` is request dedup only.** It collapses duplicate calls within a single render. It
35
+ does **not** cache across requests and is never a substitute for `next: { revalidate }`.
36
+
37
+ 5. **Every cacheable route exports `revalidate`.** Suggested tiers: listing 24h–30d, detail/city 24h
38
+ (`86400`), state/landing 7d (`604800`), WP catch-all 7d. Do **not** add `revalidate` to
39
+ mutation/personalized routes (forms, thank-you, wizards). Prefer `export const revalidate` over
40
+ `dynamic = "force-static"` so a failed ISR revalidation preserves the last good cache instead of
41
+ overwriting it with a broken page.
42
+
43
+ 6. **On-demand revalidation dual-invalidates.** `/api/revalidate` must call `revalidateTag`/
44
+ `revalidatePath` **and** the CDN invalidation (e.g. `invalidateCloudFrontPaths`) in the same
45
+ request, otherwise the CDN keeps serving stale until its own TTL.
46
+
47
+ 7. **Image config** in `next.config`: set `minimumCacheTTL: 2592000` (30d), `qualities`, and
48
+ `formats: ["image/avif", "image/webp"]`. No malformed `remotePatterns` hostnames.
49
+
50
+ 8. **Asset proxy TTLs** are type-keyed (image/css/font) at `365d + SWR 30d`.
51
+
52
+ ## Canonical snippet — API client (read vs. mutation)
53
+
54
+ ```ts
55
+ interface FetchOptions {
56
+ endpoint: string;
57
+ method?: "GET" | "POST" | "PUT" | "DELETE";
58
+ body?: object | null;
59
+ revalidateTag?: string;
60
+ revalidate?: number;
61
+ /** Writes are never cached. Reads (default) cache regardless of method. */
62
+ mutation?: boolean;
63
+ }
64
+
65
+ export async function fetchData<T>({
66
+ endpoint,
67
+ method = "GET",
68
+ body = null,
69
+ revalidateTag,
70
+ revalidate = 86400, // 24h time-based fallback; pair with tags for surgical invalidation
71
+ mutation = false,
72
+ }: FetchOptions): Promise<T | null> {
73
+ const isMutation = mutation || method === "PUT" || method === "DELETE";
74
+
75
+ const requestOptions: RequestInit = {
76
+ method,
77
+ // Reads (GET or POST) cache cross-request via `next`; mutations opt out.
78
+ ...(isMutation
79
+ ? { cache: "no-store" as RequestCache }
80
+ : { next: { revalidate, tags: revalidateTag ? [revalidateTag] : [] } }),
81
+ };
82
+
83
+ if ((method === "POST" || method === "PUT") && body) {
84
+ requestOptions.body = JSON.stringify(body);
85
+ }
86
+ // ...fetch + error handling...
87
+ }
88
+ ```
89
+
90
+ ## Canonical snippet — WordPress GraphQL client (cached POST)
91
+
92
+ ```ts
93
+ export const WP_CACHE_DURATIONS = {
94
+ pages: 86400, posts: 86400, menus: 86400, staticPages: 604800, default: 86400,
95
+ } as const;
96
+
97
+ export async function fetchWPAPI<T>(
98
+ query: string,
99
+ { variables, revalidate = WP_CACHE_DURATIONS.default, tags }: {
100
+ variables?: Record<string, unknown>; revalidate?: number; tags?: string[];
101
+ } = {},
102
+ ) {
103
+ const res = await fetch(WP_API_URL, {
104
+ method: "POST",
105
+ headers,
106
+ body: JSON.stringify({ query, variables }),
107
+ next: { revalidate, ...(tags && { tags }) }, // a POST IS cacheable WITH next options
108
+ });
109
+ }
110
+ ```
111
+
112
+ ## Do NOT
113
+
114
+ - ❌ `next: method === "GET" ? {...} : { revalidate: 0 }` — leaves POST reads uncached.
115
+ - ❌ `fetch(API_URL, { method: "POST", body })` with no `next` options (bare, uncached POST read).
116
+ - ❌ `next: { tags }` with no `revalidate`.
117
+ - ❌ Adding `export const revalidate` to a form/mutation/personalized route.
118
+ - ❌ Treating `React.cache()` as cross-request caching.
119
+ - ❌ Caching a mutation (`submit-review`, lead submit, `PUT`/`DELETE`/`PATCH`).
120
+ - ❌ Mixing `cacheComponents: true` (`"use cache"`) with route-segment `export const revalidate` —
121
+ that is a separate, deliberate migration; do not introduce it ad hoc.
@@ -0,0 +1,272 @@
1
+ ---
2
+ applyTo: "**/*.tsx"
3
+ ---
4
+ # SEO & AI Optimization Patterns
5
+
6
+ These rules ensure components are optimized for Google's generative AI features (AI Overviews, AI
7
+ Mode) and browser-agent interactions: agent-friendly HTML, a clean accessibility tree, semantic
8
+ structure, and E-E-A-T signals. Pairs with the `ai-seo-optimization` skill and the `audit-ai-seo`
9
+ prompt.
10
+
11
+ ---
12
+
13
+ ## Semantic HTML (CRITICAL)
14
+
15
+ ### Interactive Elements
16
+
17
+ **NEVER use `<div>` or `<span>` with click handlers. Use proper semantic elements.**
18
+
19
+ ```tsx
20
+ // ✅ CORRECT: Semantic button
21
+ <button onClick={handleClick} type="button">
22
+ Click me
23
+ </button>
24
+
25
+ // ✅ CORRECT: Navigation link
26
+ <Link href="/page">Go to page</Link>
27
+
28
+ // ❌ WRONG: Non-semantic clickable div
29
+ <div onClick={handleClick} className="cursor-pointer">
30
+ Click me
31
+ </div>
32
+
33
+ // ❌ WRONG: Span with role hack
34
+ <span role="button" onClick={handleClick}>
35
+ Click me
36
+ </span>
37
+ ```
38
+
39
+ ### Form Inputs
40
+
41
+ **Every input MUST have an associated label. Placeholder is NOT a substitute.**
42
+
43
+ ```tsx
44
+ // ✅ CORRECT: Label with htmlFor
45
+ <label htmlFor="email">Email address</label>
46
+ <input id="email" name="email" type="email" required aria-required="true" />
47
+
48
+ // ✅ CORRECT: Wrapped label (implicit association)
49
+ <label>
50
+ Email address
51
+ <input name="email" type="email" required />
52
+ </label>
53
+
54
+ // ❌ WRONG: Placeholder only (invisible to accessibility tree)
55
+ <input placeholder="Enter your email" type="email" />
56
+
57
+ // ❌ WRONG: aria-label without visible label (poor for agents)
58
+ <input aria-label="Email" type="email" />
59
+ ```
60
+
61
+ ### Heading Hierarchy
62
+
63
+ **Headings must follow logical order. Never skip levels.**
64
+
65
+ ```tsx
66
+ // ✅ CORRECT: Proper hierarchy
67
+ <h1>Page Title</h1>
68
+ <section>
69
+ <h2>Section Title</h2>
70
+ <h3>Subsection</h3>
71
+ </section>
72
+
73
+ // ❌ WRONG: Skipping h2
74
+ <h1>Page Title</h1>
75
+ <h3>Subsection</h3>
76
+
77
+ // ❌ WRONG: Using heading for styling only
78
+ <h4 className="text-sm font-bold">Small bold text</h4> // Use <p> with classes instead
79
+ ```
80
+
81
+ ### Lists
82
+
83
+ ```tsx
84
+ // ✅ CORRECT: Semantic list
85
+ <ul>
86
+ {items.map(item => <li key={item.id}>{item.name}</li>)}
87
+ </ul>
88
+
89
+ // ❌ WRONG: Div-based list
90
+ <div className="flex flex-col gap-2">
91
+ {items.map(item => <div key={item.id}>{item.name}</div>)}
92
+ </div>
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Accessibility for AI Agents
98
+
99
+ ### Skip-to-Content Link
100
+
101
+ The root layout MUST include a skip-to-content link as the first focusable element:
102
+
103
+ ```tsx
104
+ <body>
105
+ <a
106
+ href="#main-content"
107
+ className="sr-only focus:not-sr-only focus:absolute focus:z-50 focus:p-4 focus:bg-background focus:text-foreground"
108
+ >
109
+ Skip to main content
110
+ </a>
111
+ {/* Header/Navigation */}
112
+ <main id="main-content">
113
+ {children}
114
+ </main>
115
+ </body>
116
+ ```
117
+
118
+ ### Accessible Names
119
+
120
+ All interactive elements MUST have an accessible name:
121
+
122
+ ```tsx
123
+ // ✅ CORRECT: Button with text content (name from content)
124
+ <button>Submit form</button>
125
+
126
+ // ✅ CORRECT: Icon button with aria-label
127
+ <button aria-label="Close dialog">
128
+ <XIcon className="h-4 w-4" />
129
+ </button>
130
+
131
+ // ❌ WRONG: Icon button without accessible name
132
+ <button>
133
+ <XIcon className="h-4 w-4" />
134
+ </button>
135
+ ```
136
+
137
+ ### Image Alt Text
138
+
139
+ ```tsx
140
+ // ✅ CORRECT: Descriptive alt text
141
+ <Image alt="Two-story brick building with a landscaped courtyard entrance" src={src} />
142
+
143
+ // ✅ CORRECT: Decorative image (empty alt)
144
+ <Image alt="" src={decorativeBg} aria-hidden="true" />
145
+
146
+ // ❌ WRONG: Generic alt text
147
+ <Image alt="photo" src={src} />
148
+ <Image alt="image 1" src={src} />
149
+
150
+ // ❌ WRONG: Missing alt
151
+ <Image src={src} />
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Metadata & SEO
157
+
158
+ ### generateMetadata Pattern
159
+
160
+ Every page with dynamic content MUST implement `generateMetadata`:
161
+
162
+ ```tsx
163
+ export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
164
+ const { slug } = await params;
165
+ const data = await getData(slug);
166
+ if (!data) return {};
167
+
168
+ return {
169
+ title: data.title,
170
+ description: data.description,
171
+ openGraph: {
172
+ title: data.title,
173
+ description: data.description,
174
+ images: data.image ? [{ url: data.image }] : undefined,
175
+ },
176
+ alternates: {
177
+ canonical: getCanonicalUrl(slug),
178
+ },
179
+ };
180
+ }
181
+ ```
182
+
183
+ ### No Snippet Blocking
184
+
185
+ **NEVER add `nosnippet` to content pages.** This prevents AI feature inclusion:
186
+
187
+ ```tsx
188
+ // ❌ WRONG: Blocks AI features
189
+ export const metadata = {
190
+ robots: { index: true, follow: true, nosnippet: true },
191
+ };
192
+
193
+ // ✅ CORRECT: Allow snippets (default behavior)
194
+ export const metadata = {
195
+ robots: { index: true, follow: true },
196
+ };
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Structured Data (JSON-LD)
202
+
203
+ ### Implementation Pattern
204
+
205
+ Use a dedicated JSON-LD component in layout components:
206
+
207
+ ```tsx
208
+ export function JsonLd({ data }: { data: Record<string, unknown> }) {
209
+ return (
210
+ <script
211
+ type="application/ld+json"
212
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
213
+ />
214
+ );
215
+ }
216
+ ```
217
+
218
+ ### Required Schema by Page Type
219
+
220
+ | Page Type | Required Schema |
221
+ |-----------|----------------|
222
+ | Homepage | `Organization`, `WebSite` with `SearchAction` |
223
+ | Local business / Location | `LocalBusiness` with address, geo, rating |
224
+ | Blog Post | `Article` with author (`Person`), dates |
225
+ | Author Page | `Person` with credentials, `knowsAbout` |
226
+ | FAQ Section | `FAQPage` with Q&A pairs |
227
+ | All Pages | `BreadcrumbList` |
228
+
229
+ ---
230
+
231
+ ## Layout Stability (CLS Prevention)
232
+
233
+ ```tsx
234
+ // ✅ CORRECT: Explicit dimensions prevent layout shift
235
+ <Image src={src} alt="..." width={800} height={600} />
236
+
237
+ // ✅ CORRECT: Aspect ratio container
238
+ <div className="aspect-video relative">
239
+ <Image src={src} alt="..." fill className="object-cover" />
240
+ </div>
241
+
242
+ // ❌ WRONG: No dimensions (causes layout shift)
243
+ <img src={src} alt="..." />
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Server-Side Rendering
249
+
250
+ **Critical content MUST render on the server.** Client Components should only wrap interactive UI, not content:
251
+
252
+ ```tsx
253
+ // ✅ CORRECT: Content in Server Component
254
+ export default async function DetailPage({ params }: Props) {
255
+ const item = await getItem(params.slug);
256
+ return (
257
+ <main id="main-content">
258
+ <h1>{item.name}</h1>
259
+ <p>{item.description}</p>
260
+ <ContactForm itemId={item.id} /> {/* Client Component for form only */}
261
+ </main>
262
+ );
263
+ }
264
+
265
+ // ❌ WRONG: Entire page as Client Component
266
+ "use client";
267
+ export default function DetailPage() {
268
+ const [item, setItem] = useState(null);
269
+ useEffect(() => { fetchItem().then(setItem); }, []);
270
+ // Content invisible to crawlers until JS executes
271
+ }
272
+ ```
@@ -0,0 +1,58 @@
1
+ # Release — Node / npm Partial
2
+
3
+ Version-bump and quality-check mechanics for **Node.js / npm packages** (anything with a
4
+ `package.json`). Included by `prepare-github-release` when the project is detected as a Node package.
5
+
6
+ ## Detection signal
7
+
8
+ - A `package.json` exists at the repo root **and** there is no WordPress plugin header
9
+ (`*.php` file with a `Version:` plugin header). If both are present, prefer the WordPress partial.
10
+
11
+ ## Version source of truth
12
+
13
+ | File | What to update |
14
+ |------|----------------|
15
+ | `package.json` | `"version": "X.Y.Z"` |
16
+ | `package-lock.json` | top-level `version` **and** the root package entry under `packages[""].version` |
17
+
18
+ Prefer the npm tooling so both files stay in sync automatically:
19
+
20
+ ```bash
21
+ # Bumps package.json + package-lock.json and (by default) creates a commit + tag.
22
+ # Use --no-git-tag-version to keep the version change uncommitted so it can go in the release branch/PR.
23
+ npm version X.Y.Z --no-git-tag-version
24
+ ```
25
+
26
+ If you edit `package.json` by hand instead, re-sync the lockfile without touching dependencies:
27
+
28
+ ```bash
29
+ npm install --package-lock-only
30
+ ```
31
+
32
+ ## Quality checks (run BEFORE bumping)
33
+
34
+ Inspect `package.json` `scripts` and run whatever exists — do not assume script names:
35
+
36
+ ```bash
37
+ npm ci # clean install when a lockfile is present (CI parity)
38
+ npm test --if-present
39
+ npm run lint --if-present
40
+ npm run type-check --if-present
41
+ npm run build --if-present
42
+ ```
43
+
44
+ Do **not** proceed if any check fails.
45
+
46
+ ## What the release usually produces
47
+
48
+ - **npm publish** — most Node packages publish to the npm registry. The publish step almost always
49
+ lives in a workflow triggered by a GitHub Release (`on: release: [created|published]`) or a tag
50
+ push — confirm via the workflow-analysis step in the orchestrator before assuming a bare tag is enough.
51
+ - Optional build artifacts (bundled `dist/`, types) attached to the Release.
52
+
53
+ ## Notes
54
+
55
+ - `private: true` in `package.json` means the package is **not** published to npm — the release is
56
+ tag/GitHub-Release only. Surface this to the user.
57
+ - The npm version in `package.json` is the source of truth that `publish.yml`-style workflows read,
58
+ so it must be committed before the tag/Release is created.
@@ -0,0 +1,56 @@
1
+ # Release — WordPress Plugin Partial
2
+
3
+ Version-bump and quality-check mechanics for **Silver Assist WordPress plugins**. Included by
4
+ `prepare-github-release` when the project is detected as a WordPress plugin. See the
5
+ `release-management` skill for full documentation and troubleshooting.
6
+
7
+ ## Detection signal
8
+
9
+ - A `*.php` file with a plugin header (`* Version: X.Y.Z`) at the repo root, usually alongside a
10
+ `composer.json` and often a `readme.txt`.
11
+
12
+ ## Version source of truth
13
+
14
+ | File | What to update |
15
+ |------|----------------|
16
+ | Main plugin file | `* Version: X.Y.Z` header **and** the `VERSION` constant if defined |
17
+ | `readme.txt` | `Stable tag: X.Y.Z` (if present) |
18
+ | `composer.json` | `"version": "X.Y.Z"` (if present) |
19
+
20
+ Prefer the bundled script so every file stays in sync:
21
+
22
+ ```bash
23
+ ./scripts/update-version.sh X.Y.Z
24
+ # Some plugins ship the simple variant instead:
25
+ ./scripts/update-version-simple.sh X.Y.Z
26
+ ```
27
+
28
+ ## Quality checks (run BEFORE bumping)
29
+
30
+ Do **not** proceed if any check fails:
31
+
32
+ ```bash
33
+ ./scripts/run-quality-checks.sh --skip-wp-setup phpcs phpstan
34
+ ```
35
+
36
+ If the script is absent, fall back to the tools directly:
37
+
38
+ ```bash
39
+ composer run phpcs --if-present
40
+ composer run phpstan --if-present
41
+ composer run test --if-present
42
+ ```
43
+
44
+ ## What the release usually produces
45
+
46
+ - **Distributable ZIP** — the GitHub Actions release workflow builds the plugin ZIP and attaches it
47
+ to the GitHub Release. This almost always triggers on `on: release: [created|published]`, so a bare
48
+ tag is typically **not** enough — confirm via the workflow-analysis step in the orchestrator.
49
+ - No npm publish; WordPress plugins are distributed as ZIP artifacts (and/or wordpress.org SVN).
50
+
51
+ ## Notes
52
+
53
+ - Some plugins use `master` instead of `main` as the default branch (e.g.
54
+ `silver-assist-post-revalidate`) — always verify with `gh repo view --json defaultBranchRef`.
55
+ - The plugin-header version is what WordPress and the release workflow read, so it must be committed
56
+ before the tag/Release is created.
@@ -85,3 +85,4 @@ npm run build --if-present
85
85
  - [ ] No sensitive data exposed (API keys, secrets)
86
86
  - [ ] No `any` types introduced
87
87
  - [ ] JSDoc comments on new functions
88
+ - [ ] If data fetching / routes / `next.config` changed: caching follows `caching.instructions.md` (reads cache regardless of method, mutations `no-store`, cacheable routes export `revalidate`)