wordpress-agent-kit 0.3.0 → 0.3.2

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 (31) hide show
  1. package/.github/skills/blueprint/SKILL.md +418 -0
  2. package/.github/skills/wp-abilities-api/SKILL.md +12 -0
  3. package/.github/skills/wp-abilities-api/references/delegate-helper-pattern.md +241 -0
  4. package/.github/skills/wp-abilities-api/references/domain-vs-projection.md +113 -0
  5. package/.github/skills/wp-abilities-api/references/error-code-vocabulary.md +123 -0
  6. package/.github/skills/wp-abilities-api/references/grouping-heuristic.md +89 -0
  7. package/.github/skills/wp-abilities-api/references/input-schema-gotchas.md +265 -0
  8. package/.github/skills/wp-abilities-api/references/php-registration.md +47 -20
  9. package/.github/skills/wp-abilities-api/references/plugin-family-patterns.md +233 -0
  10. package/.github/skills/wp-abilities-api/references/shared-core-service.md +184 -0
  11. package/.github/skills/wp-abilities-audit/SKILL.md +199 -0
  12. package/.github/skills/wp-abilities-audit/references/audit-schema.md +300 -0
  13. package/.github/skills/wp-abilities-audit/references/capability-gate-tracing.md +197 -0
  14. package/.github/skills/wp-abilities-audit/references/controller-enumeration.md +116 -0
  15. package/.github/skills/wp-abilities-verify/SKILL.md +215 -0
  16. package/.github/skills/wp-abilities-verify/references/annotation-correctness.md +154 -0
  17. package/.github/skills/wp-abilities-verify/references/audit-schema-validation.md +131 -0
  18. package/.github/skills/wp-abilities-verify/references/permission-roundtrip.md +190 -0
  19. package/.github/skills/wp-abilities-verify/references/runtime-harness.md +462 -0
  20. package/.github/skills/wp-abilities-verify/references/schema-lints.md +118 -0
  21. package/.github/skills/wp-abilities-verify/references/static-enumeration.md +126 -0
  22. package/.github/skills/wp-plugin-directory-guidelines/SKILL.md +133 -0
  23. package/.github/skills/wp-plugin-directory-guidelines/references/gpl-compliance.md +217 -0
  24. package/.github/skills/wp-plugin-directory-guidelines/references/guideline-review-checklist.md +592 -0
  25. package/.github/skills/wp-plugin-directory-guidelines/references/naming-rules.md +121 -0
  26. package/.github/skills/wp-project-triage/scripts/detect_wp_project.mjs +22 -4
  27. package/README.md +6 -3
  28. package/dist/lib/api.js +30 -19
  29. package/dist/lib/installer.js +0 -2
  30. package/extensions/wp-agent-kit/index.ts +146 -324
  31. package/package.json +1 -1
@@ -1,51 +1,57 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
1
3
  /**
2
- * WordPress Agent Kit Pi Extension
4
+ * WordPress Agent Kit Pi Extension
3
5
  *
4
- * Provides Pi agents with WordPress development tools:
5
- * - WordPress agent skills (13 skills covering plugin/theme/block dev, REST API, WP-CLI, etc.)
6
+ * Provides Pi Coding Agent with WordPress development tools:
7
+ * - 17 WordPress agent skills (plugin/theme/block dev, REST API, WP-CLI, etc.)
6
8
  * - Project triage detection
7
- * - Skill installation and syncing
8
- * - Version upgrade management
9
+ * - Skill installation, syncing, and upgrade management
9
10
  *
10
- * Compatible with Pi Coding Agent SDK (extensions.md spec).
11
+ * Follows Pi Coding Agent SDK conventions (extensions.md, packages.md, skills.md).
11
12
  */
12
13
  import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
13
14
  import { Type } from 'typebox';
14
15
 
15
- // Import programmatic API for WordPress Agent Kit operations
16
- import {
17
- type InstallOptions,
18
- type SyncOptions,
19
- type TriageOptions,
20
- computeChanges,
21
- installKitApi,
22
- isKitInstalled,
23
- loadManifest,
24
- runTriageApi,
25
- syncSkillsApi,
26
- } from '../../dist/lib/api.js';
16
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
+ const PACKAGE_ROOT = path.resolve(__dirname, '..', '..');
18
+
19
+ // Resolve the API import dynamically — works in both dev (src/) and npm (dist/)
20
+ let apiModule: typeof import('../../dist/lib/api.js');
21
+ try {
22
+ apiModule = await import('../../dist/lib/api.js');
23
+ } catch {
24
+ // Fallback for dev: import from source via jiti
25
+ apiModule = (await import('../../src/lib/api.js')) as typeof import('../../dist/lib/api.js');
26
+ }
27
+
28
+ const { installKitApi, syncSkillsApi, runTriageApi, isKitInstalled, loadManifest } = apiModule;
27
29
 
28
30
  export default function (pi: ExtensionAPI) {
29
31
  // =========================================================================
30
32
  // Skills Registration
31
33
  // =========================================================================
32
- // Register WordPress skills via resources_discover so Pi agents can use them.
33
- // Skills are loaded from .github/skills/ (also declared in package.json pi.skills).
34
34
  pi.on('resources_discover', async (_event, _ctx) => {
35
- // Skills are auto-discovered from pi.skills in package.json.
36
- // No additional configuration needed here — the manifest handles it.
37
- return {};
35
+ const skillsDir = path.join(PACKAGE_ROOT, '.github', 'skills');
36
+ const promptsDir = path.join(PACKAGE_ROOT, '.github', 'prompts');
37
+ return {
38
+ skillPaths: [skillsDir],
39
+ promptPaths: [promptsDir],
40
+ };
38
41
  });
39
42
 
40
43
  // =========================================================================
41
44
  // Session notifications
42
45
  // =========================================================================
43
46
  pi.on('session_start', async (_event, ctx) => {
44
- const cwd = ctx.cwd;
45
- if (isKitInstalled(cwd, 'github')) {
46
- const manifest = loadManifest(cwd, 'github');
47
- if (manifest) {
48
- ctx.ui.setStatus('wp-agent-kit', `WP Agent Kit v${manifest.version}`);
47
+ const platforms: Array<'pi' | 'github'> = ['pi', 'github'];
48
+ for (const p of platforms) {
49
+ if (isKitInstalled(ctx.cwd, p)) {
50
+ const manifest = loadManifest(ctx.cwd, p);
51
+ if (manifest) {
52
+ ctx.ui.setStatus('wp-agent-kit', `WP Agent Kit v${manifest.version}`);
53
+ }
54
+ break;
49
55
  }
50
56
  }
51
57
  });
@@ -55,37 +61,29 @@ export default function (pi: ExtensionAPI) {
55
61
  // =========================================================================
56
62
 
57
63
  // --- wp_triage ---
58
- // Run WordPress project triage to detect project type, signals, and tooling.
59
64
  pi.registerTool({
60
65
  name: 'wp_triage',
61
66
  label: 'WP Triage',
62
67
  description:
63
- 'Run WordPress project detection to classify the codebase (plugin, theme, block theme, WP core, Gutenberg). Returns project kind, signals (block.json, Interactivity API, WP-CLI, REST API), and tooling (PHP/Node). Use before making changes to a WordPress project.',
68
+ 'Detect WordPress project type (plugin, theme, block theme, site, Gutenberg), signals (block.json, Interactivity API, WP-CLI, REST API), and tooling (PHP/Node). Use before making changes to any WordPress project.',
64
69
  promptSnippet: 'Detect WordPress project type, signals, and tooling',
65
70
  promptGuidelines: [
66
- 'Use wp_triage before making changes to any WordPress project to understand the codebase structure.',
71
+ 'Use wp_triage before making changes to any WordPress project to understand the codebase.',
67
72
  'Use wp_triage output to route to the correct WordPress development skill.',
68
73
  ],
69
74
  parameters: Type.Object({
70
75
  targetDir: Type.Optional(
71
76
  Type.String({
72
- description: 'Target directory to triage (defaults to current working directory)',
73
- })
74
- ),
75
- platform: Type.Optional(
76
- Type.String({
77
- description:
78
- 'Platform where skills are installed (github, cursor, claude, agent, pi). Default: github',
77
+ description: 'WordPress project directory (defaults to current working directory)',
79
78
  })
80
79
  ),
81
80
  }),
82
- async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
83
- const options: TriageOptions = {
84
- targetDir: params.targetDir || process.cwd(),
85
- platform: (params.platform as TriageOptions['platform']) || 'github',
86
- };
87
-
88
- const result = await runTriageApi(options);
81
+ async execute(_callId, params, _signal, _onUpdate, _ctx) {
82
+ const targetDir = params.targetDir || process.cwd();
83
+ const result = await runTriageApi({
84
+ targetDir,
85
+ platform: 'pi',
86
+ });
89
87
 
90
88
  if (!result.success) {
91
89
  return {
@@ -100,18 +98,18 @@ export default function (pi: ExtensionAPI) {
100
98
  };
101
99
  }
102
100
 
103
- const triage = result.data;
101
+ const t = result.data;
104
102
  return {
105
103
  content: [
106
104
  {
107
105
  type: 'text',
108
106
  text: JSON.stringify(
109
107
  {
110
- project: triage.project,
111
- signals: triage.signals,
112
- tooling: triage.tooling,
113
- recommendation: triage.project
114
- ? `This appears to be a ${triage.project.primary} project (confidence: ${triage.project.confidence}). Use the appropriate wp-* skill for development.`
108
+ project: t.project,
109
+ signals: t.signals,
110
+ tooling: t.tooling,
111
+ recommendation: t.project
112
+ ? `${t.project.primary} project (confidence: ${t.project.confidence}). Use the matching wp-* skill.`
115
113
  : 'Could not determine project type. Review signals manually.',
116
114
  },
117
115
  null,
@@ -119,108 +117,87 @@ export default function (pi: ExtensionAPI) {
119
117
  ),
120
118
  },
121
119
  ],
122
- details: triage,
120
+ details: t,
123
121
  };
124
122
  },
125
123
  });
126
124
 
127
125
  // --- wp_install_kit ---
128
- // Install WordPress Agent Kit skills into a target directory.
129
126
  pi.registerTool({
130
127
  name: 'wp_install_kit',
131
128
  label: 'WP Install Kit',
132
129
  description:
133
- 'Install the WordPress Agent Kit into a project directory. Copies .github/skills (13 WordPress development skills), agent definitions, instructions, and AGENTS.md template. Supports safe update mode that preserves user modifications. Use when setting up a new WordPress project or adding AI agent support.',
130
+ 'Install WordPress Agent Kit into a project directory. Copies 17 WordPress development skills, agent definitions, workflow instructions, and an AGENTS.md template. Safe by default preserves user modifications on re-run.',
134
131
  promptSnippet: 'Install WordPress AI agent skills and configuration into a project',
135
132
  promptGuidelines: [
136
133
  'Use wp_install_kit when setting up a new WordPress project for AI agent development.',
137
- 'Use wp_install_kit with dryRun:true to preview changes before applying.',
134
+ 'Use wp_install_kit with dryRun: true to preview changes before applying.',
138
135
  ],
139
136
  parameters: Type.Object({
140
137
  targetDir: Type.String({
141
- description: 'Target directory to install into (the WordPress project root)',
138
+ description: 'WordPress project root directory to install into',
142
139
  }),
143
- platform: Type.Optional(
144
- Type.String({
145
- description: 'Target platform: github, cursor, claude, agent, or pi. Default: github',
146
- })
147
- ),
148
140
  dryRun: Type.Optional(
149
141
  Type.Boolean({
150
- description: 'If true, preview changes without making them. Default: false',
142
+ description: 'Preview changes without applying (default: false)',
151
143
  })
152
144
  ),
153
145
  force: Type.Optional(
154
146
  Type.Boolean({
155
- description: 'Overwrite user modifications on update. Default: false',
147
+ description: 'Overwrite user modifications on update (default: false)',
156
148
  })
157
149
  ),
158
150
  }),
159
- async execute(_toolCallId, params, _signal, onUpdate, _ctx) {
160
- const options: InstallOptions = {
151
+ async execute(_callId, params, _signal, onUpdate, _ctx) {
152
+ const opts = {
161
153
  targetDir: params.targetDir,
162
- platform: (params.platform as InstallOptions['platform']) || 'github',
154
+ platform: 'pi' as const,
163
155
  dryRun: params.dryRun ?? false,
164
156
  force: params.force ?? false,
165
- safe: !params.force, // Default: safe unless forced
157
+ safe: !(params.force ?? false),
166
158
  backup: true,
167
159
  };
168
160
 
169
- if (options.dryRun) {
170
- onUpdate?.({
171
- content: [
172
- {
173
- type: 'text',
174
- text: 'Computing changes (dry-run)...',
175
- },
176
- ],
177
- });
161
+ if (opts.dryRun) {
162
+ onUpdate?.({ content: [{ type: 'text', text: 'Computing changes (dry-run)...' }] });
178
163
  }
179
164
 
180
- const result = await installKitApi(options);
165
+ const result = await installKitApi(opts);
181
166
 
182
167
  if (!result.success) {
183
168
  return {
184
169
  content: [
185
- {
186
- type: 'text',
187
- text: `Install failed: ${result.error?.message || 'Unknown error'}`,
188
- },
170
+ { type: 'text', text: `Install failed: ${result.error?.message || 'Unknown error'}` },
189
171
  ],
190
- details: { error: result.error },
191
172
  isError: true,
192
173
  };
193
174
  }
194
175
 
195
- // Handle dry-run results
196
- if (options.dryRun && 'wouldExecute' in (result.data || {})) {
197
- const dryRun = result.data as {
176
+ // Dry-run: show change preview
177
+ if (opts.dryRun && 'wouldExecute' in (result.data || {})) {
178
+ const dr = result.data as {
198
179
  wouldExecute: boolean;
199
180
  actions: Array<{ type: string; description: string }>;
200
- summary: Record<string, unknown>;
201
181
  };
202
182
  return {
203
183
  content: [
204
184
  {
205
185
  type: 'text',
206
186
  text: [
207
- '# WordPress Agent Kit — Dry Run Preview',
208
- '',
209
- `Target: ${options.targetDir}`,
210
- `Platform: ${options.platform}`,
187
+ '# WordPress Agent Kit — Dry Run',
211
188
  '',
212
189
  '## Changes',
213
- ...dryRun.actions.map((a) => `- ${a.type}: ${a.description}`),
190
+ ...dr.actions.map((a) => `- **${a.type}**: ${a.description}`),
214
191
  '',
215
- 'Run without dryRun to apply these changes.',
192
+ 'Run without `dryRun` to apply.',
216
193
  ].join('\n'),
217
194
  },
218
195
  ],
219
- details: dryRun,
196
+ details: dr,
220
197
  };
221
198
  }
222
199
 
223
- // Handle real results
200
+ // Real result
224
201
  const data = result.data as {
225
202
  filesCreated: string[];
226
203
  filesSkipped: string[];
@@ -230,40 +207,28 @@ export default function (pi: ExtensionAPI) {
230
207
  };
231
208
 
232
209
  const lines = [
233
- data.isUpdate ? '# WordPress Agent Kit — Updated' : '# WordPress Agent Kit — Installed',
210
+ data.isUpdate ? '# Updated' : '# Installed',
234
211
  '',
235
- `Target: ${options.targetDir}`,
236
- `Platform: ${options.platform}`,
237
- `Files created/updated: ${data.filesCreated.length}`,
212
+ `**Target**: ${opts.targetDir}`,
213
+ `**Files**: ${data.filesCreated.length} created/updated`,
238
214
  ];
239
215
 
240
216
  if (data.filesSkipped.length > 0) {
241
- lines.push(`Files preserved: ${data.filesSkipped.length} (user-modified)`);
242
- for (const skipped of data.filesSkipped.slice(0, 10)) {
243
- lines.push(` - ${skipped}`);
244
- }
217
+ lines.push(`**Preserved**: ${data.filesSkipped.length} files (user-modified)`);
245
218
  }
246
219
 
247
- if (data.conflicts && data.conflicts.length > 0) {
248
- lines.push(
249
- '',
250
- `⚠ ${data.conflicts.length} conflict(s) — re-run with force:true to overwrite:`
251
- );
252
- for (const conflict of data.conflicts.slice(0, 5)) {
253
- lines.push(` - ${conflict}`);
254
- }
220
+ if (data.conflicts?.length) {
221
+ lines.push('', '⚠ **Conflicts** (re-run with `force: true` to overwrite):');
222
+ for (const c of data.conflicts.slice(0, 5)) lines.push(`- ${c}`);
255
223
  }
256
224
 
257
- if (data.backupDir) {
258
- lines.push('', `Backup created: ${data.backupDir}`);
259
- }
225
+ if (data.backupDir) lines.push('', `**Backup**: ${data.backupDir}`);
260
226
 
261
227
  lines.push(
262
228
  '',
263
229
  '## Next Steps',
264
- '1. Run wp_triage to detect project type',
265
- '2. Review AGENTS.md for project-specific guidance',
266
- '3. Use `/skill:wp-project-triage` to begin development'
230
+ '1. Run `wp_triage` to detect project type',
231
+ '2. Review `AGENTS.md` for guidance'
267
232
  );
268
233
 
269
234
  return {
@@ -274,109 +239,45 @@ export default function (pi: ExtensionAPI) {
274
239
  });
275
240
 
276
241
  // --- wp_sync_skills ---
277
- // Sync WordPress skills from upstream WordPress/agent-skills repo.
278
242
  pi.registerTool({
279
243
  name: 'wp_sync_skills',
280
244
  label: 'WP Sync Skills',
281
245
  description:
282
- 'Sync WordPress agent skills from the upstream WordPress/agent-skills repository into the local project. Fetches the latest skill definitions and replaces the local .github/skills directory. Use when skills need updating from upstream.',
246
+ 'Sync WordPress agent skills from the upstream WordPress/agent-skills repository. Fetches the latest 17 skill definitions and replaces the local skills directory.',
283
247
  promptSnippet: 'Sync latest WordPress agent skills from upstream',
284
248
  promptGuidelines: [
285
249
  'Use wp_sync_skills to pull the latest WordPress development skills from WordPress/agent-skills.',
286
- 'Use wp_sync_skills with dryRun:true to preview before syncing.',
287
250
  ],
288
251
  parameters: Type.Object({
289
- targetDir: Type.Optional(
290
- Type.String({
291
- description: 'Target directory (defaults to the project root running wp-agent-kit)',
292
- })
293
- ),
294
252
  ref: Type.Optional(
295
253
  Type.String({
296
254
  description: 'Git ref to sync from (default: trunk)',
297
255
  })
298
256
  ),
299
- dryRun: Type.Optional(
300
- Type.Boolean({
301
- description: 'If true, preview without applying. Default: false',
302
- })
303
- ),
304
257
  }),
305
- async execute(_toolCallId, params, _signal, onUpdate, _ctx) {
306
- const options: SyncOptions = {
307
- targetDir: params.targetDir || process.cwd(),
308
- ref: params.ref || 'trunk',
309
- dryRun: params.dryRun ?? false,
310
- };
258
+ async execute(_callId, params, _signal, onUpdate, _ctx) {
259
+ onUpdate?.({ content: [{ type: 'text', text: 'Syncing from WordPress/agent-skills...' }] });
311
260
 
312
- onUpdate?.({
313
- content: [
314
- {
315
- type: 'text',
316
- text: params.dryRun
317
- ? 'Previewing skill sync (dry-run)...'
318
- : `Syncing skills from WordPress/agent-skills@${options.ref}...`,
319
- },
320
- ],
261
+ const result = await syncSkillsApi({
262
+ targetDir: PACKAGE_ROOT,
263
+ ref: params.ref || 'trunk',
321
264
  });
322
265
 
323
- const result = await syncSkillsApi(options);
324
-
325
266
  if (!result.success) {
326
267
  return {
327
268
  content: [
328
- {
329
- type: 'text',
330
- text: `Sync failed: ${result.error?.message || 'Unknown error'}`,
331
- },
269
+ { type: 'text', text: `Sync failed: ${result.error?.message || 'Unknown error'}` },
332
270
  ],
333
- details: { error: result.error },
334
271
  isError: true,
335
272
  };
336
273
  }
337
274
 
338
- if (options.dryRun && 'wouldExecute' in (result.data || {})) {
339
- const dryRun = result.data as {
340
- wouldExecute: boolean;
341
- actions: Array<{ type: string; description: string }>;
342
- };
343
- return {
344
- content: [
345
- {
346
- type: 'text',
347
- text: [
348
- '# Skill Sync — Dry Run',
349
- '',
350
- `Source: WordPress/agent-skills@${options.ref}`,
351
- '',
352
- '## Actions',
353
- ...dryRun.actions.map((a) => `- ${a.type}: ${a.description}`),
354
- '',
355
- 'Run without dryRun to apply.',
356
- ].join('\n'),
357
- },
358
- ],
359
- details: dryRun,
360
- };
361
- }
362
-
363
- const data = result.data as {
364
- skillsSynced: number;
365
- method: string;
366
- sourceUrl: string;
367
- };
368
-
275
+ const data = result.data as { skillsSynced: number; method: string };
369
276
  return {
370
277
  content: [
371
278
  {
372
279
  type: 'text',
373
- text: [
374
- '# Skills Synced',
375
- '',
376
- `Skills synced: ${data.skillsSynced}`,
377
- `Method: ${data.method}`,
378
- `Source: ${data.sourceUrl}`,
379
- ].join('\n'),
280
+ text: `# Skills Synced\n\n**Synced**: ${data.skillsSynced} skills\n**Method**: ${data.method}`,
380
281
  },
381
282
  ],
382
283
  details: data,
@@ -385,60 +286,44 @@ export default function (pi: ExtensionAPI) {
385
286
  });
386
287
 
387
288
  // --- wp_upgrade ---
388
- // Check and apply WordPress Agent Kit upgrades.
389
289
  pi.registerTool({
390
290
  name: 'wp_upgrade',
391
291
  label: 'WP Upgrade',
392
292
  description:
393
- 'Check for WordPress Agent Kit updates and apply them. Shows current vs latest version, detects installed platforms, and can preview or apply the upgrade. Use to keep agent skills and configuration current.',
394
- promptSnippet: 'Check and apply WordPress Agent Kit version upgrades',
293
+ 'Check for WordPress Agent Kit updates. Shows current vs latest version and can preview or apply upgrades.',
294
+ promptSnippet: 'Check WordPress Agent Kit version',
395
295
  promptGuidelines: [
396
296
  'Use wp_upgrade to check if the installed WordPress Agent Kit is up to date.',
397
- 'Use wp_upgrade with checkOnly:true to preview without applying.',
297
+ 'Use wp_upgrade with checkOnly: true to preview without applying.',
398
298
  ],
399
299
  parameters: Type.Object({
400
300
  targetDir: Type.Optional(
401
301
  Type.String({
402
- description: 'Target directory to check/upgrade (defaults to current working directory)',
302
+ description: 'Project directory to check (defaults to cwd)',
403
303
  })
404
304
  ),
405
305
  checkOnly: Type.Optional(
406
306
  Type.Boolean({
407
- description: 'Only check for updates, do not apply. Default: false',
408
- })
409
- ),
410
- force: Type.Optional(
411
- Type.Boolean({
412
- description: 'Overwrite user modifications on upgrade. Default: false',
307
+ description: 'Only check for updates, do not apply (default: false)',
413
308
  })
414
309
  ),
415
310
  }),
416
- async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
311
+ async execute(_callId, params, _signal, _onUpdate, _ctx) {
417
312
  const targetDir = params.targetDir || process.cwd();
418
- const force = params.force ?? false;
419
313
  const checkOnly = params.checkOnly ?? false;
314
+ const manifest = loadManifest(targetDir, 'pi') || loadManifest(targetDir, 'github');
315
+ const currentVersion = manifest?.version || 'not installed';
316
+ const latestVersion = (() => {
317
+ try {
318
+ const fs = require('node:fs');
319
+ return JSON.parse(fs.readFileSync(path.join(PACKAGE_ROOT, 'package.json'), 'utf-8'))
320
+ .version;
321
+ } catch {
322
+ return 'unknown';
323
+ }
324
+ })();
420
325
 
421
- // Detect installed platforms and current version
422
- const fs = await import('node:fs');
423
- const path = await import('node:path');
424
- const pkgPath = path.join(
425
- path.dirname(new URL(import.meta.url).pathname),
426
- '..',
427
- '..',
428
- '..',
429
- 'package.json'
430
- );
431
- let latestVersion = 'unknown';
432
- try {
433
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
434
- latestVersion = pkg.version;
435
- } catch {
436
- // fallback
437
- }
438
-
439
- const manifest = loadManifest(targetDir, 'github');
440
- const currentVersion = manifest?.version || 'unknown';
441
- const isInstalled = isKitInstalled(targetDir, 'github');
326
+ const installed = isKitInstalled(targetDir, 'pi') || isKitInstalled(targetDir, 'github');
442
327
 
443
328
  if (checkOnly) {
444
329
  return {
@@ -448,57 +333,39 @@ export default function (pi: ExtensionAPI) {
448
333
  text: [
449
334
  '# WordPress Agent Kit — Version Check',
450
335
  '',
451
- `Target: ${targetDir}`,
452
- `Installed: ${isInstalled ? 'yes' : 'no'}`,
453
- `Current version: ${currentVersion}`,
454
- `Latest version: ${latestVersion}`,
336
+ `**Installed**: ${installed ? 'yes' : 'no'}`,
337
+ `**Current**: ${currentVersion}`,
338
+ `**Latest**: ${latestVersion}`,
455
339
  '',
456
- currentVersion !== latestVersion && isInstalled
457
- ? '⚠ Update available. Run without checkOnly to apply.'
458
- : isInstalled
459
- ? ' Up to date.'
460
- : 'Not installed. Use wp_install_kit to install.',
340
+ currentVersion !== latestVersion && installed
341
+ ? '⚠ Update available. Run without `checkOnly` to apply.'
342
+ : installed
343
+ ? ' Up to date.'
344
+ : 'Not installed. Use `wp_install_kit` to install.',
461
345
  ].join('\n'),
462
346
  },
463
347
  ],
464
- details: {
465
- targetDir,
466
- currentVersion,
467
- latestVersion,
468
- isInstalled,
469
- },
348
+ details: { currentVersion, latestVersion, installed },
470
349
  };
471
350
  }
472
351
 
473
- // Apply upgrade
474
- if (!isInstalled) {
352
+ if (!installed) {
475
353
  return {
476
- content: [
477
- {
478
- type: 'text',
479
- text: 'Kit not installed. Use wp_install_kit to install first.',
480
- },
481
- ],
354
+ content: [{ type: 'text', text: 'Kit not installed. Use `wp_install_kit` first.' }],
482
355
  isError: true,
483
356
  };
484
357
  }
485
358
 
486
359
  const result = await installKitApi({
487
360
  targetDir,
488
- platform: 'github',
489
- force,
490
- safe: !force,
361
+ platform: 'pi',
362
+ safe: true,
491
363
  backup: true,
492
364
  });
493
365
 
494
366
  if (!result.success) {
495
367
  return {
496
- content: [
497
- {
498
- type: 'text',
499
- text: `Upgrade failed: ${result.error?.message || 'Unknown error'}`,
500
- },
501
- ],
368
+ content: [{ type: 'text', text: `Upgrade failed: ${result.error?.message}` }],
502
369
  isError: true,
503
370
  };
504
371
  }
@@ -507,28 +374,17 @@ export default function (pi: ExtensionAPI) {
507
374
  filesCreated: string[];
508
375
  filesSkipped: string[];
509
376
  conflicts?: string[];
510
- backupDir: string | null;
511
377
  };
512
-
513
378
  const lines = [
514
- '# WordPress Agent Kit — Upgraded',
379
+ '# Upgraded',
515
380
  '',
516
- `Version: ${currentVersion} → ${latestVersion}`,
517
- `Files updated: ${data.filesCreated.length}`,
381
+ `**Version**: ${currentVersion} → ${latestVersion}`,
382
+ `**Files updated**: ${data.filesCreated.length}`,
518
383
  ];
519
-
520
- if (data.filesSkipped.length > 0) {
521
- lines.push(`Files preserved: ${data.filesSkipped.length} (user-modified)`);
522
- }
523
- if (data.conflicts && data.conflicts.length > 0) {
524
- lines.push(
525
- '',
526
- `⚠ ${data.conflicts.length} conflict(s) — re-run with force:true to overwrite.`
527
- );
528
- }
529
- if (data.backupDir) {
530
- lines.push(`Backup: ${data.backupDir}`);
531
- }
384
+ if (data.filesSkipped.length)
385
+ lines.push(`**Preserved**: ${data.filesSkipped.length} (user-modified)`);
386
+ if (data.conflicts?.length)
387
+ lines.push(`⚠ ${data.conflicts.length} conflicts (use force: true)`);
532
388
 
533
389
  return {
534
390
  content: [{ type: 'text', text: lines.join('\n') }],
@@ -546,85 +402,51 @@ export default function (pi: ExtensionAPI) {
546
402
  handler: async (args, ctx) => {
547
403
  const targetDir = args?.trim() || ctx.cwd;
548
404
  ctx.ui.setStatus('wp-triage', 'Running triage...');
549
- const result = await runTriageApi({ targetDir });
550
-
405
+ const result = await runTriageApi({ targetDir, platform: 'pi' });
551
406
  if (!result.success) {
552
407
  ctx.ui.notify(`Triage failed: ${result.error?.message}`, 'error');
553
408
  return;
554
409
  }
555
-
556
- const triage = result.data;
557
- const summary = triage.project
558
- ? `${triage.project.primary} (confidence: ${triage.project.confidence})`
410
+ const t = result.data;
411
+ const summary = t.project
412
+ ? `${t.project.primary} (confidence: ${t.project.confidence})`
559
413
  : 'unknown';
560
414
  ctx.ui.notify(`Project: ${summary}`, 'info');
561
- ctx.ui.setStatus('wp-triage', `Detected: ${summary}`);
415
+ ctx.ui.setStatus('wp-triage', summary);
562
416
  },
563
417
  });
564
418
 
565
419
  pi.registerCommand('wp-install', {
566
- description: 'Install WordPress Agent Kit into a project',
420
+ description: 'Install WordPress Agent Kit into current project',
567
421
  handler: async (args, ctx) => {
568
422
  const targetDir = args?.trim() || ctx.cwd;
569
423
  ctx.ui.setStatus('wp-install', 'Installing...');
570
- const result = await installKitApi({
571
- targetDir,
572
- platform: 'github',
573
- safe: true,
574
- backup: true,
575
- });
576
-
424
+ const result = await installKitApi({ targetDir, platform: 'pi', safe: true, backup: true });
577
425
  if (!result.success) {
578
426
  ctx.ui.notify(`Install failed: ${result.error?.message}`, 'error');
579
427
  return;
580
428
  }
581
-
582
429
  const data = result.data as { filesCreated: string[]; isUpdate: boolean };
583
- const verb = data.isUpdate ? 'Updated' : 'Installed';
584
- ctx.ui.notify(`${verb} (${data.filesCreated.length} files)`, 'info');
585
- ctx.ui.setStatus('wp-install', `${verb} ${data.filesCreated.length} files`);
430
+ ctx.ui.notify(
431
+ `${data.isUpdate ? 'Updated' : 'Installed'} (${data.filesCreated.length} entries)`,
432
+ 'info'
433
+ );
434
+ ctx.ui.setStatus('wp-agent-kit', 'v0.3.0 installed');
586
435
  },
587
436
  });
588
437
 
589
438
  pi.registerCommand('wp-sync-skills', {
590
439
  description: 'Sync WordPress skills from upstream',
591
440
  handler: async (args, ctx) => {
592
- ctx.ui.setStatus('wp-sync', 'Syncing skills...');
593
- const result = await syncSkillsApi({
594
- ref: args?.trim() || 'trunk',
595
- });
596
-
441
+ ctx.ui.setStatus('wp-sync', 'Syncing...');
442
+ const result = await syncSkillsApi({ targetDir: PACKAGE_ROOT, ref: args?.trim() || 'trunk' });
597
443
  if (!result.success) {
598
444
  ctx.ui.notify(`Sync failed: ${result.error?.message}`, 'error');
599
445
  return;
600
446
  }
601
-
602
- const data = result.data as { skillsSynced: number; method: string };
603
- ctx.ui.notify(`Synced ${data.skillsSynced} skills (${data.method})`, 'info');
604
- ctx.ui.setStatus('wp-sync', `${data.skillsSynced} skills`);
605
- },
606
- });
607
-
608
- pi.registerCommand('wp-upgrade', {
609
- description: 'Check or apply WordPress Agent Kit upgrades',
610
- handler: async (_args, ctx) => {
611
- const targetDir = ctx.cwd;
612
- const manifest = loadManifest(targetDir, 'github');
613
- const currentVersion = manifest?.version || 'not installed';
614
-
615
- const pkgPath = `${ctx.cwd}/node_modules/wordpress-agent-kit/package.json`;
616
- let latestVersion = 'unknown';
617
- try {
618
- const fs = await import('node:fs');
619
- if (fs.existsSync(pkgPath)) {
620
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
621
- latestVersion = pkg.version;
622
- }
623
- } catch {
624
- // fallback
625
- }
626
-
627
- ctx.ui.notify(`WP Agent Kit: ${currentVersion} → ${latestVersion}`, 'info');
447
+ const data = result.data as { skillsSynced: number };
448
+ ctx.ui.notify(`Synced ${data.skillsSynced} skills`, 'info');
449
+ ctx.ui.setStatus('wp-agent-kit', `${data.skillsSynced} skills`);
628
450
  },
629
451
  });
630
452
  }