wordpress-agent-kit 0.4.0 → 0.6.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/.agents/skills/wp-bootstrap/SKILL.md +314 -0
- package/.agents/skills/wp-bootstrap/references/composer-setup.md +275 -0
- package/.agents/skills/wp-bootstrap/references/monorepo-patterns.md +184 -0
- package/.agents/skills/wp-bootstrap/scripts/bootstrap.sh +151 -0
- package/.agents/skills/wp-bootstrap/scripts/detect-structure.mjs +466 -0
- package/.agents/skills/wp-bootstrap/scripts/package-wp.sh +173 -0
- package/.agents/skills/wp-bootstrap/scripts/playground-start.sh +148 -0
- package/.agents/skills/wp-bootstrap/scripts/playground-verify.sh +165 -0
- package/.agents/skills/wp-bootstrap/scripts/setup-github.sh +417 -0
- package/{.github → .agents}/skills/wp-wpcli-and-ops/SKILL.md +11 -9
- package/.agents/skills/wp-wpengine/SKILL.md +462 -0
- package/.agents/skills/wp-wpengine/references/ci-gate.md +469 -0
- package/.agents/skills/wp-wpengine/references/github-actions-deploy.md +743 -0
- package/.agents/skills/wp-wpengine/scripts/ci-gate.sh +118 -0
- package/.agents/skills/wp-wpengine/scripts/wpe-check.sh +89 -0
- package/.agents/skills/wp-wpengine/scripts/wpe-preflight.sh +104 -0
- package/.github/agents/wp-architect.agent.md +1 -2
- package/.github/copilot-instructions.md +1 -1
- package/.github/instructions/wordpress-workflow.instructions.md +3 -3
- package/AGENTS.md +22 -10
- package/AGENTS.template.md +20 -10
- package/README.md +89 -85
- package/dist/cli.js +7 -1
- package/dist/commands/bootstrap.js +105 -0
- package/dist/commands/clean-skills.js +64 -0
- package/dist/commands/setup.js +6 -2
- package/dist/commands/sync-skills.js +3 -0
- package/dist/lib/api.js +165 -5
- package/dist/lib/bootstrap.js +352 -0
- package/dist/lib/installer.js +166 -2
- package/extensions/wp-agent-kit/index.ts +325 -10
- package/package.json +10 -14
- package/skills-custom/wp-bootstrap/SKILL.md +314 -0
- package/skills-custom/wp-bootstrap/references/composer-setup.md +275 -0
- package/skills-custom/wp-bootstrap/references/monorepo-patterns.md +184 -0
- package/skills-custom/wp-bootstrap/scripts/bootstrap.sh +151 -0
- package/skills-custom/wp-bootstrap/scripts/detect-structure.mjs +466 -0
- package/skills-custom/wp-bootstrap/scripts/package-wp.sh +173 -0
- package/skills-custom/wp-bootstrap/scripts/playground-start.sh +148 -0
- package/skills-custom/wp-bootstrap/scripts/playground-verify.sh +165 -0
- package/skills-custom/wp-bootstrap/scripts/setup-github.sh +417 -0
- package/skills-custom/wp-wpengine/SKILL.md +362 -27
- package/skills-custom/wp-wpengine/references/ci-gate.md +469 -0
- package/skills-custom/wp-wpengine/references/github-actions-deploy.md +743 -0
- package/skills-custom/wp-wpengine/scripts/ci-gate.sh +118 -0
- package/skills-custom/wp-wpengine/scripts/wpe-check.sh +89 -0
- package/skills-custom/wp-wpengine/scripts/wpe-preflight.sh +104 -0
- package/.github/skills/wp-wpengine/SKILL.md +0 -127
- package/.github/workflows/ci.yml +0 -44
- package/.husky/pre-commit +0 -7
- package/CLI_REVIEW.md +0 -250
- package/biome.json +0 -39
- /package/{.github → .agents}/skills/blueprint/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wordpress-router/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wordpress-router/references/decision-tree.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-api/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-api/references/delegate-helper-pattern.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-api/references/domain-vs-projection.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-api/references/error-code-vocabulary.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-api/references/grouping-heuristic.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-api/references/input-schema-gotchas.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-api/references/php-registration.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-api/references/plugin-family-patterns.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-api/references/rest-api.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-api/references/shared-core-service.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-audit/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-audit/references/audit-schema.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-audit/references/capability-gate-tracing.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-audit/references/controller-enumeration.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-verify/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-verify/references/annotation-correctness.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-verify/references/audit-schema-validation.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-verify/references/permission-roundtrip.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-verify/references/runtime-harness.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-verify/references/schema-lints.md +0 -0
- /package/{.github → .agents}/skills/wp-abilities-verify/references/static-enumeration.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/references/attributes-and-serialization.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/references/block-json.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/references/creating-new-blocks.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/references/debugging.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/references/deprecations.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/references/dynamic-rendering.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/references/inner-blocks.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/references/registration.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/references/supports-and-wrappers.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/references/tooling-and-testing.md +0 -0
- /package/{.github → .agents}/skills/wp-block-development/scripts/list_blocks.mjs +0 -0
- /package/{.github → .agents}/skills/wp-block-themes/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-block-themes/references/creating-new-block-theme.md +0 -0
- /package/{.github → .agents}/skills/wp-block-themes/references/debugging.md +0 -0
- /package/{.github → .agents}/skills/wp-block-themes/references/patterns.md +0 -0
- /package/{.github → .agents}/skills/wp-block-themes/references/style-variations.md +0 -0
- /package/{.github → .agents}/skills/wp-block-themes/references/templates-and-parts.md +0 -0
- /package/{.github → .agents}/skills/wp-block-themes/references/theme-json.md +0 -0
- /package/{.github → .agents}/skills/wp-block-themes/scripts/detect_block_themes.mjs +0 -0
- /package/{.github → .agents}/skills/wp-interactivity-api/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-interactivity-api/references/debugging.md +0 -0
- /package/{.github → .agents}/skills/wp-interactivity-api/references/directives-quickref.md +0 -0
- /package/{.github → .agents}/skills/wp-interactivity-api/references/server-side-rendering.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/references/autoload-options.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/references/cron.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/references/database.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/references/http-api.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/references/measurement.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/references/object-cache.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/references/query-monitor-headless.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/references/server-timing.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/references/wp-cli-doctor.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/references/wp-cli-profile.md +0 -0
- /package/{.github → .agents}/skills/wp-performance/scripts/perf_inspect.mjs +0 -0
- /package/{.github → .agents}/skills/wp-phpstan/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-phpstan/references/configuration.md +0 -0
- /package/{.github → .agents}/skills/wp-phpstan/references/third-party-classes.md +0 -0
- /package/{.github → .agents}/skills/wp-phpstan/references/wordpress-annotations.md +0 -0
- /package/{.github → .agents}/skills/wp-phpstan/scripts/phpstan_inspect.mjs +0 -0
- /package/{.github → .agents}/skills/wp-playground/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-playground/references/blueprints.md +0 -0
- /package/{.github → .agents}/skills/wp-playground/references/cli-commands.md +0 -0
- /package/{.github → .agents}/skills/wp-playground/references/debugging.md +0 -0
- /package/{.github → .agents}/skills/wp-playground/references/e2e-playwright.md +0 -0
- /package/{.github → .agents}/skills/wp-plugin-development/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-plugin-development/references/data-and-cron.md +0 -0
- /package/{.github → .agents}/skills/wp-plugin-development/references/debugging.md +0 -0
- /package/{.github → .agents}/skills/wp-plugin-development/references/lifecycle.md +0 -0
- /package/{.github → .agents}/skills/wp-plugin-development/references/security.md +0 -0
- /package/{.github → .agents}/skills/wp-plugin-development/references/settings-api.md +0 -0
- /package/{.github → .agents}/skills/wp-plugin-development/references/structure.md +0 -0
- /package/{.github → .agents}/skills/wp-plugin-development/scripts/detect_plugins.mjs +0 -0
- /package/{.github → .agents}/skills/wp-plugin-directory-guidelines/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-plugin-directory-guidelines/references/gpl-compliance.md +0 -0
- /package/{.github → .agents}/skills/wp-plugin-directory-guidelines/references/guideline-review-checklist.md +0 -0
- /package/{.github → .agents}/skills/wp-plugin-directory-guidelines/references/naming-rules.md +0 -0
- /package/{.github → .agents}/skills/wp-project-triage/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-project-triage/references/triage.schema.json +0 -0
- /package/{.github → .agents}/skills/wp-project-triage/scripts/detect_wp_project.mjs +0 -0
- /package/{.github → .agents}/skills/wp-rest-api/SKILL.md +0 -0
- /package/{.github → .agents}/skills/wp-rest-api/references/authentication.md +0 -0
- /package/{.github → .agents}/skills/wp-rest-api/references/custom-content-types.md +0 -0
- /package/{.github → .agents}/skills/wp-rest-api/references/discovery-and-params.md +0 -0
- /package/{.github → .agents}/skills/wp-rest-api/references/responses-and-fields.md +0 -0
- /package/{.github → .agents}/skills/wp-rest-api/references/routes-and-endpoints.md +0 -0
- /package/{.github → .agents}/skills/wp-rest-api/references/schema.md +0 -0
- /package/{.github → .agents}/skills/wp-wpcli-and-ops/references/automation.md +0 -0
- /package/{.github → .agents}/skills/wp-wpcli-and-ops/references/cron-and-cache.md +0 -0
- /package/{.github → .agents}/skills/wp-wpcli-and-ops/references/debugging.md +0 -0
- /package/{.github → .agents}/skills/wp-wpcli-and-ops/references/multisite.md +0 -0
- /package/{.github → .agents}/skills/wp-wpcli-and-ops/references/packages-and-updates.md +0 -0
- /package/{.github → .agents}/skills/wp-wpcli-and-ops/references/safety.md +0 -0
- /package/{.github → .agents}/skills/wp-wpcli-and-ops/references/search-replace.md +0 -0
- /package/{.github → .agents}/skills/wp-wpcli-and-ops/scripts/wpcli_inspect.mjs +0 -0
- /package/{.github → .agents}/skills/wpds/SKILL.md +0 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* detect-structure.mjs — probe a repo and emit a JSON structure report.
|
|
4
|
+
*
|
|
5
|
+
* Answers: Is this a monorepo? Where are the WP packages? Where is the WP
|
|
6
|
+
* root (if any)? What tooling exists? What WP Engine remotes are configured?
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node detect-structure.mjs [dir] # probe dir (default: cwd)
|
|
10
|
+
* node detect-structure.mjs --json [dir] # always emit raw JSON (default)
|
|
11
|
+
* node detect-structure.mjs --pretty [dir] # human-readable summary
|
|
12
|
+
*
|
|
13
|
+
* Exit code: 0 always (probe never fails; missing fields are null/[]).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { execSync } from 'node:child_process';
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
|
|
20
|
+
const args = process.argv.slice(2);
|
|
21
|
+
const pretty = args.includes('--pretty');
|
|
22
|
+
const dirArg = args.find(a => !a.startsWith('--')) ?? '.';
|
|
23
|
+
const root = path.resolve(dirArg);
|
|
24
|
+
|
|
25
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
const exists = (...parts) => fs.existsSync(path.join(root, ...parts));
|
|
28
|
+
const read = (...parts) => {
|
|
29
|
+
try { return fs.readFileSync(path.join(root, ...parts), 'utf-8'); }
|
|
30
|
+
catch { return null; }
|
|
31
|
+
};
|
|
32
|
+
const readJson = (...parts) => {
|
|
33
|
+
try { return JSON.parse(read(...parts) ?? 'null'); }
|
|
34
|
+
catch { return null; }
|
|
35
|
+
};
|
|
36
|
+
const run = (cmd, cwd = root) => {
|
|
37
|
+
try { return execSync(cmd, { cwd, encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] }).trim(); }
|
|
38
|
+
catch { return null; }
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Recursively find files matching predicate up to maxDepth
|
|
42
|
+
function findFiles(dir, predicate, maxDepth = 3, _depth = 0) {
|
|
43
|
+
const results = [];
|
|
44
|
+
if (_depth > maxDepth) return results;
|
|
45
|
+
let entries;
|
|
46
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
|
|
47
|
+
catch { return results; }
|
|
48
|
+
for (const e of entries) {
|
|
49
|
+
const full = path.join(dir, e.name);
|
|
50
|
+
if (e.isDirectory()) {
|
|
51
|
+
if (['node_modules','vendor','.git','dist','build','.next','coverage'].includes(e.name)) continue;
|
|
52
|
+
results.push(...findFiles(full, predicate, maxDepth, _depth + 1));
|
|
53
|
+
} else if (predicate(e.name, full)) {
|
|
54
|
+
results.push(full);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return results;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── 1. WordPress package detection ───────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
function parsePluginHeader(content) {
|
|
63
|
+
const get = (key) => {
|
|
64
|
+
const m = content.match(new RegExp(`\\*\\s+${key}:\\s*(.+)`, 'i'));
|
|
65
|
+
return m ? m[1].trim() : null;
|
|
66
|
+
};
|
|
67
|
+
return { name: get('Plugin Name'), version: get('Version'), textDomain: get('Text Domain') };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function parseThemeHeader(content) {
|
|
71
|
+
const get = (key) => {
|
|
72
|
+
const m = content.match(new RegExp(`\\*\\s+${key}:\\s*(.+)`, 'i'));
|
|
73
|
+
return m ? m[1].trim() : null;
|
|
74
|
+
};
|
|
75
|
+
return { name: get('Theme Name'), version: get('Version'), textDomain: get('Text Domain') };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function detectWpPackages() {
|
|
79
|
+
const packages = [];
|
|
80
|
+
|
|
81
|
+
// Find all PHP files that could be plugin/theme headers (shallow)
|
|
82
|
+
const phpFiles = findFiles(root, (name) => name.endsWith('.php'), 2);
|
|
83
|
+
const checkedDirs = new Set();
|
|
84
|
+
|
|
85
|
+
for (const phpFile of phpFiles) {
|
|
86
|
+
const dir = path.dirname(phpFile);
|
|
87
|
+
const relDir = path.relative(root, dir);
|
|
88
|
+
if (checkedDirs.has(relDir)) continue;
|
|
89
|
+
|
|
90
|
+
const content = fs.readFileSync(phpFile, 'utf-8');
|
|
91
|
+
|
|
92
|
+
if (/Plugin Name:/i.test(content)) {
|
|
93
|
+
const h = parsePluginHeader(content);
|
|
94
|
+
const composerJson = readJson(...relDir.split('/'), 'composer.json') ?? readJson('composer.json');
|
|
95
|
+
checkedDirs.add(relDir);
|
|
96
|
+
packages.push({
|
|
97
|
+
type: 'plugin',
|
|
98
|
+
path: relDir || '.',
|
|
99
|
+
name: h.name,
|
|
100
|
+
version: h.version,
|
|
101
|
+
slug: h.textDomain ?? path.basename(dir),
|
|
102
|
+
mainFile: path.relative(dir, phpFile),
|
|
103
|
+
hasComposer: exists(...relDir.split('/'), 'composer.json'),
|
|
104
|
+
hasTests: exists(...relDir.split('/'), 'tests') || exists(...relDir.split('/'), 'test'),
|
|
105
|
+
hasPest: (readJson(...relDir.split('/'), 'composer.json') ?? {})?.['require-dev']?.['pestphp/pest'] != null,
|
|
106
|
+
});
|
|
107
|
+
} else if (/Theme Name:/i.test(content)) {
|
|
108
|
+
const h = parseThemeHeader(content);
|
|
109
|
+
checkedDirs.add(relDir);
|
|
110
|
+
packages.push({
|
|
111
|
+
type: 'theme',
|
|
112
|
+
path: relDir || '.',
|
|
113
|
+
name: h.name,
|
|
114
|
+
version: h.version,
|
|
115
|
+
slug: h.textDomain ?? path.basename(dir),
|
|
116
|
+
hasComposer: exists(...relDir.split('/'), 'composer.json'),
|
|
117
|
+
styleSheet: path.relative(dir, phpFile),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Also detect by style.css (theme) in case main file is not PHP
|
|
123
|
+
const styleCss = findFiles(root, (name) => name === 'style.css', 2);
|
|
124
|
+
for (const cssFile of styleCss) {
|
|
125
|
+
const dir = path.dirname(cssFile);
|
|
126
|
+
const relDir = path.relative(root, dir);
|
|
127
|
+
if (checkedDirs.has(relDir)) continue;
|
|
128
|
+
const content = fs.readFileSync(cssFile, 'utf-8');
|
|
129
|
+
if (/Theme Name:/i.test(content)) {
|
|
130
|
+
const h = parseThemeHeader(content);
|
|
131
|
+
checkedDirs.add(relDir);
|
|
132
|
+
packages.push({
|
|
133
|
+
type: 'theme',
|
|
134
|
+
path: relDir || '.',
|
|
135
|
+
name: h.name,
|
|
136
|
+
version: h.version,
|
|
137
|
+
slug: h.textDomain ?? path.basename(dir),
|
|
138
|
+
hasComposer: exists(...relDir.split('/'), 'composer.json'),
|
|
139
|
+
styleSheet: path.relative(dir, cssFile),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return packages;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── 2. WP root detection ─────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
function detectWpRoot() {
|
|
150
|
+
// Look for wp-config.php or wp-blog-header.php
|
|
151
|
+
const candidates = ['.', 'web', 'public', 'wordpress', 'wp', 'src/wordpress'];
|
|
152
|
+
for (const c of candidates) {
|
|
153
|
+
if (exists(c, 'wp-config.php') || exists(c, 'wp-blog-header.php')) {
|
|
154
|
+
return c === '.' ? '.' : c;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// Look up to 2 levels deep
|
|
158
|
+
const configs = findFiles(root, (name) => name === 'wp-config.php', 2);
|
|
159
|
+
if (configs.length > 0) {
|
|
160
|
+
return path.relative(root, path.dirname(configs[0]));
|
|
161
|
+
}
|
|
162
|
+
return null; // Playground-only / external WP
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── 3. WP Engine remotes ──────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
function detectWpeRemotes() {
|
|
168
|
+
const output = run('git remote -v');
|
|
169
|
+
if (!output) return [];
|
|
170
|
+
const remotes = [];
|
|
171
|
+
const seen = new Set();
|
|
172
|
+
for (const line of output.split('\n')) {
|
|
173
|
+
const m = line.match(/^(\S+)\s+(git@git\.wpengine\.com:[^\s]+)/);
|
|
174
|
+
if (m && !seen.has(m[1])) {
|
|
175
|
+
seen.add(m[1]);
|
|
176
|
+
const urlParts = m[2].match(/git@git\.wpengine\.com:(?:(production|staging|development)\/)?([^.]+)\.git/);
|
|
177
|
+
remotes.push({
|
|
178
|
+
name: m[1],
|
|
179
|
+
url: m[2],
|
|
180
|
+
environment: urlParts?.[1] ?? null,
|
|
181
|
+
install: urlParts?.[2] ?? null,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return remotes;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ── 4. Tooling detection ──────────────────────────────────────────────────────
|
|
189
|
+
|
|
190
|
+
function detectPackageManager() {
|
|
191
|
+
if (exists('pnpm-lock.yaml') || exists('pnpm-workspace.yaml')) return 'pnpm';
|
|
192
|
+
if (exists('yarn.lock')) return 'yarn';
|
|
193
|
+
if (exists('bun.lockb')) return 'bun';
|
|
194
|
+
if (exists('package-lock.json')) return 'npm';
|
|
195
|
+
if (exists('package.json')) return 'npm';
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function detectJsWorkspaces() {
|
|
200
|
+
const ws = [];
|
|
201
|
+
// pnpm workspace
|
|
202
|
+
const pnpmWs = read('pnpm-workspace.yaml');
|
|
203
|
+
if (pnpmWs) {
|
|
204
|
+
const pkgs = [...pnpmWs.matchAll(/[-]\s+['"]?([^'"#\n]+)['"]?/g)].map(m => m[1].trim());
|
|
205
|
+
ws.push(...pkgs);
|
|
206
|
+
}
|
|
207
|
+
// npm/yarn workspaces
|
|
208
|
+
const rootPkg = readJson('package.json');
|
|
209
|
+
if (rootPkg?.workspaces) {
|
|
210
|
+
ws.push(...(Array.isArray(rootPkg.workspaces) ? rootPkg.workspaces : rootPkg.workspaces.packages ?? []));
|
|
211
|
+
}
|
|
212
|
+
return ws;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function detectPhpTooling() {
|
|
216
|
+
const composerJson = readJson('composer.json');
|
|
217
|
+
const devDeps = { ...(composerJson?.require ?? {}), ...(composerJson?.['require-dev'] ?? {}) };
|
|
218
|
+
return {
|
|
219
|
+
hasComposer: exists('composer.json'),
|
|
220
|
+
hasPhpcs: exists('vendor/bin/phpcs') || 'squizlabs/php_codesniffer' in devDeps,
|
|
221
|
+
hasWpcs: 'wp-coding-standards/wpcs' in devDeps,
|
|
222
|
+
hasPhpstan: exists('vendor/bin/phpstan') || 'phpstan/phpstan' in devDeps,
|
|
223
|
+
hasPhpstanWp: 'szepeviktor/phpstan-wordpress' in devDeps,
|
|
224
|
+
hasPest: 'pestphp/pest' in devDeps,
|
|
225
|
+
phpcsConfig: exists('phpcs.xml.dist') ? 'phpcs.xml.dist' : exists('phpcs.xml') ? 'phpcs.xml' : null,
|
|
226
|
+
phpstanConfig: exists('phpstan.neon.dist') ? 'phpstan.neon.dist' : exists('phpstan.neon') ? 'phpstan.neon' : null,
|
|
227
|
+
composerScripts: Object.keys(composerJson?.scripts ?? {}),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function detectJsTooling() {
|
|
232
|
+
const rootPkg = readJson('package.json');
|
|
233
|
+
const biomeJson = readJson('biome.json');
|
|
234
|
+
const devDeps = { ...(rootPkg?.devDependencies ?? {}), ...(rootPkg?.dependencies ?? {}) };
|
|
235
|
+
return {
|
|
236
|
+
hasBiome: !!biomeJson || '@biomejs/biome' in devDeps,
|
|
237
|
+
biomeVersion: biomeJson?.['$schema']?.match(/schemas\/(\d+\.\d+\.\d+)/)?.[1] ?? (devDeps['@biomejs/biome']?.replace(/^\^|~/, '') ?? null),
|
|
238
|
+
hasEslint: 'eslint' in devDeps || exists('eslint.config.mjs') || exists('.eslintrc.js'),
|
|
239
|
+
hasPrettier: 'prettier' in devDeps || exists('.prettierrc'),
|
|
240
|
+
hasVitest: 'vitest' in devDeps,
|
|
241
|
+
hasJest: 'jest' in devDeps,
|
|
242
|
+
hasPlaywright: '@playwright/test' in devDeps || exists('playwright.config.ts') || exists('playwright.config.js'),
|
|
243
|
+
hasWpScripts: '@wordpress/scripts' in devDeps,
|
|
244
|
+
rootScripts: Object.keys(rootPkg?.scripts ?? {}),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function detectGitHooks() {
|
|
249
|
+
if (exists('.githooks')) return '.githooks';
|
|
250
|
+
if (exists('.husky')) return '.husky';
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function detectPlayground() {
|
|
255
|
+
const blueprints = findFiles(root, (name) => name.endsWith('-blueprint.json') || name === 'blueprint.json', 3);
|
|
256
|
+
const runScript = findFiles(root, (name) => name.startsWith('run-') && name.endsWith('.sh'), 3)
|
|
257
|
+
.filter(f => fs.readFileSync(f,'utf-8').includes('@wp-playground'));
|
|
258
|
+
return {
|
|
259
|
+
hasPlayground: blueprints.length > 0 || runScript.length > 0,
|
|
260
|
+
blueprints: blueprints.map(f => path.relative(root, f)),
|
|
261
|
+
scripts: runScript.map(f => path.relative(root, f)),
|
|
262
|
+
hasWpEnv: exists('.wp-env.json'),
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function detectSatispress() {
|
|
267
|
+
const composerJson = readJson('composer.json');
|
|
268
|
+
const authJson = readJson('auth.json');
|
|
269
|
+
const repos = Object.values(composerJson?.repositories ?? {});
|
|
270
|
+
const satisRepo = repos.find(r => typeof r === 'object' && r.url?.includes('satispress'));
|
|
271
|
+
return {
|
|
272
|
+
configured: !!satisRepo,
|
|
273
|
+
url: satisRepo?.url ?? null,
|
|
274
|
+
hasAuthJson: !!authJson,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function detectWpackagist() {
|
|
279
|
+
const composerJson = readJson('composer.json');
|
|
280
|
+
const repos = Object.values(composerJson?.repositories ?? {});
|
|
281
|
+
return repos.some(r => typeof r === 'object' && r.url?.includes('wpackagist.org'));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ── 5. GitHub CLI detection ─────────────────────────────────────────────────
|
|
285
|
+
|
|
286
|
+
function detectGithub() {
|
|
287
|
+
// Check if gh is installed
|
|
288
|
+
const ghInstalled = !!run('command -v gh 2>/dev/null || which gh 2>/dev/null');
|
|
289
|
+
if (!ghInstalled) {
|
|
290
|
+
return { ghInstalled: false, authenticated: false, account: null,
|
|
291
|
+
repoOwner: null, repoName: null, repoUrl: null, visibility: null,
|
|
292
|
+
defaultBranch: null, existingSecrets: [], missingSecrets: [],
|
|
293
|
+
branchProtection: {}, error: 'gh CLI not installed' };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Parse owner/repo from git remote origin
|
|
297
|
+
const originUrl = run('git remote get-url origin 2>/dev/null');
|
|
298
|
+
let repoOwner = null, repoName = null;
|
|
299
|
+
if (originUrl) {
|
|
300
|
+
const sshMatch = originUrl.match(/git@github\.com[:/]([^/]+)\/([^.]+)(?:\.git)?$/);
|
|
301
|
+
const httpsMatch = originUrl.match(/https?:\/\/github\.com\/([^/]+)\/([^/.]+)/);
|
|
302
|
+
const m = sshMatch ?? httpsMatch;
|
|
303
|
+
if (m) { repoOwner = m[1]; repoName = m[2]; }
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Check authentication
|
|
307
|
+
const authOut = run('gh auth status 2>&1');
|
|
308
|
+
const authenticated = authOut ? /Logged in to github\.com/.test(authOut) : false;
|
|
309
|
+
const accountMatch = authOut?.match(/account (\S+)/);
|
|
310
|
+
const account = accountMatch?.[1] ?? null;
|
|
311
|
+
|
|
312
|
+
if (!authenticated || !repoOwner || !repoName) {
|
|
313
|
+
return { ghInstalled: true, authenticated, account, repoOwner, repoName,
|
|
314
|
+
repoUrl: repoOwner && repoName ? `https://github.com/${repoOwner}/${repoName}` : null,
|
|
315
|
+
visibility: null, defaultBranch: null, existingSecrets: [], missingSecrets: [],
|
|
316
|
+
branchProtection: {}, error: authenticated ? 'no GitHub remote found' : 'not authenticated' };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Repo info
|
|
320
|
+
let repoInfo = null;
|
|
321
|
+
try {
|
|
322
|
+
const info = run(`gh repo view ${repoOwner}/${repoName} --json name,owner,url,defaultBranchRef,visibility 2>/dev/null`);
|
|
323
|
+
if (info) repoInfo = JSON.parse(info);
|
|
324
|
+
} catch {}
|
|
325
|
+
|
|
326
|
+
const defaultBranch = repoInfo?.defaultBranchRef?.name ?? 'main';
|
|
327
|
+
const visibility = repoInfo?.visibility ?? null;
|
|
328
|
+
const repoUrl = repoInfo?.url ?? `https://github.com/${repoOwner}/${repoName}`;
|
|
329
|
+
|
|
330
|
+
// Existing secrets
|
|
331
|
+
const secretsOut = run(`gh secret list --repo ${repoOwner}/${repoName} --json name 2>/dev/null`);
|
|
332
|
+
let existingSecrets = [];
|
|
333
|
+
try { existingSecrets = secretsOut ? JSON.parse(secretsOut).map(s => s.name) : []; } catch {}
|
|
334
|
+
|
|
335
|
+
// Required secrets for WP Engine deploy
|
|
336
|
+
const REQUIRED_SECRETS = [
|
|
337
|
+
'WPE_SSH_KEY', 'WPE_SSH_KNOWN_HOSTS',
|
|
338
|
+
'WPE_PROD_INSTALL', 'WPE_PROD_GIT_URL',
|
|
339
|
+
'WPE_STAGING_INSTALL', 'WPE_STAGING_GIT_URL',
|
|
340
|
+
'WPE_DEV_INSTALL', 'WPE_DEV_GIT_URL',
|
|
341
|
+
'WPE_API_USER', 'WPE_API_PASSWORD',
|
|
342
|
+
];
|
|
343
|
+
const existingSet = new Set(existingSecrets);
|
|
344
|
+
const missingSecrets = REQUIRED_SECRETS.filter(s => !existingSet.has(s));
|
|
345
|
+
|
|
346
|
+
// Branch protection check
|
|
347
|
+
const branchProtection = {};
|
|
348
|
+
for (const branch of [defaultBranch, 'staging', 'develop']) {
|
|
349
|
+
const protOut = run(`gh api repos/${repoOwner}/${repoName}/branches/${branch}/protection 2>/dev/null`);
|
|
350
|
+
try {
|
|
351
|
+
const prot = protOut ? JSON.parse(protOut) : null;
|
|
352
|
+
branchProtection[branch] = prot && !prot.message ? {
|
|
353
|
+
protected: true,
|
|
354
|
+
requiredChecks: prot.required_status_checks?.contexts ?? [],
|
|
355
|
+
requiredReviewers: prot.required_pull_request_reviews?.required_approving_review_count ?? 0,
|
|
356
|
+
enforceAdmins: prot.enforce_admins?.enabled ?? false,
|
|
357
|
+
allowForcePushes: prot.allow_force_pushes?.enabled ?? true,
|
|
358
|
+
} : { protected: false };
|
|
359
|
+
} catch {
|
|
360
|
+
branchProtection[branch] = { protected: false };
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return {
|
|
365
|
+
ghInstalled: true, authenticated, account,
|
|
366
|
+
repoOwner, repoName, repoUrl, visibility, defaultBranch,
|
|
367
|
+
existingSecrets, missingSecrets, branchProtection,
|
|
368
|
+
error: null,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// ── 6. Assemble result ────────────────────────────────────────────────────────
|
|
373
|
+
|
|
374
|
+
const wpPackages = detectWpPackages();
|
|
375
|
+
const wpRoot = detectWpRoot();
|
|
376
|
+
const wpeRemotes = detectWpeRemotes();
|
|
377
|
+
const pm = detectPackageManager();
|
|
378
|
+
const jsWorkspaces = detectJsWorkspaces();
|
|
379
|
+
const php = detectPhpTooling();
|
|
380
|
+
const js = detectJsTooling();
|
|
381
|
+
const playground = detectPlayground();
|
|
382
|
+
const gitHooks = detectGitHooks();
|
|
383
|
+
|
|
384
|
+
const result = {
|
|
385
|
+
repoRoot: root,
|
|
386
|
+
isMonorepo: wpPackages.length > 1 || jsWorkspaces.length > 0,
|
|
387
|
+
packageManager: pm,
|
|
388
|
+
jsWorkspaces,
|
|
389
|
+
|
|
390
|
+
wpPackages,
|
|
391
|
+
wpRoot,
|
|
392
|
+
wpRootExists: wpRoot !== null,
|
|
393
|
+
playgroundOnly: wpRoot === null,
|
|
394
|
+
|
|
395
|
+
wpeRemotes,
|
|
396
|
+
hasWpeRemote: wpeRemotes.length > 0,
|
|
397
|
+
|
|
398
|
+
php,
|
|
399
|
+
js,
|
|
400
|
+
playground,
|
|
401
|
+
|
|
402
|
+
satispress: detectSatispress(),
|
|
403
|
+
wpackagist: detectWpackagist(),
|
|
404
|
+
|
|
405
|
+
gitHooks,
|
|
406
|
+
hasAgentKit: exists('.wp-agent-kit-manifest.github.json') ||
|
|
407
|
+
exists('.wp-agent-kit-manifest.pi.json') ||
|
|
408
|
+
exists('.agents', 'skills'),
|
|
409
|
+
hasAgentsDir: exists('.agents'),
|
|
410
|
+
hasAgentsMd: exists('AGENTS.md'),
|
|
411
|
+
|
|
412
|
+
gitBranch: run('git rev-parse --abbrev-ref HEAD'),
|
|
413
|
+
gitRemotes: (() => {
|
|
414
|
+
const out = run('git remote -v');
|
|
415
|
+
if (!out) return [];
|
|
416
|
+
const seen = new Set();
|
|
417
|
+
return out.split('\n')
|
|
418
|
+
.map(l => l.match(/^(\S+)\s+(\S+)\s+\(fetch\)/))
|
|
419
|
+
.filter(Boolean)
|
|
420
|
+
.filter(m => !seen.has(m[1]) && seen.add(m[1]))
|
|
421
|
+
.map(m => ({ name: m[1], url: m[2] }));
|
|
422
|
+
})(),
|
|
423
|
+
|
|
424
|
+
github: detectGithub(),
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// ── 6. Output ─────────────────────────────────────────────────────────────────
|
|
428
|
+
|
|
429
|
+
if (pretty) {
|
|
430
|
+
const pkg = (p) => ` ${p.type === 'plugin' ? '🔌' : '🎨'} ${p.name ?? p.slug} (${p.path}) v${p.version ?? '?'}`;
|
|
431
|
+
console.log(`\n── WordPress Project Structure ─────────────────────────────`);
|
|
432
|
+
console.log(` Repo root: ${root}`);
|
|
433
|
+
console.log(` Monorepo: ${result.isMonorepo ? 'yes' : 'no'}`);
|
|
434
|
+
console.log(` Package mgr: ${pm ?? 'none detected'}`);
|
|
435
|
+
console.log(` WP root: ${wpRoot ?? 'none (Playground-only)'}`);
|
|
436
|
+
console.log(`\n WP packages (${wpPackages.length}):`);
|
|
437
|
+
wpPackages.forEach(p => console.log(pkg(p)));
|
|
438
|
+
console.log(`\n WP Engine remotes (${wpeRemotes.length}):`);
|
|
439
|
+
wpeRemotes.forEach(r => console.log(` → ${r.name}: ${r.url} (install: ${r.install})`));
|
|
440
|
+
console.log(`\n PHP tooling: PHPCS=${php.hasPhpcs} PHPSTAN=${php.hasPhpstan} Pest=${php.hasPest}`);
|
|
441
|
+
console.log(` JS tooling: Biome=${js.hasBiome} Vitest=${js.hasVitest} Playwright=${js.hasPlaywright}`);
|
|
442
|
+
console.log(` Playground: ${playground.hasPlayground ? `yes (${playground.blueprints.length} blueprints)` : 'no'}`);
|
|
443
|
+
console.log(` SatisPress: ${result.satispress.configured ? result.satispress.url : 'not configured'}`);
|
|
444
|
+
console.log(` WPackagist: ${result.wpackagist ? 'yes' : 'no'}`);
|
|
445
|
+
console.log(` Agent kit: ${result.hasAgentKit ? 'installed' : 'not installed'}`);
|
|
446
|
+
console.log(` Git hooks: ${gitHooks ?? 'none'}`);
|
|
447
|
+
|
|
448
|
+
// GitHub section
|
|
449
|
+
const gh = result.github;
|
|
450
|
+
console.log(`\n GitHub CLI: ${gh.ghInstalled ? `installed (${gh.authenticated ? `✓ ${gh.account}` : '✗ not authenticated'})` : '✗ not installed'}`);
|
|
451
|
+
if (gh.ghInstalled && gh.authenticated && gh.repoOwner) {
|
|
452
|
+
console.log(` GitHub repo: ${gh.repoUrl} (${gh.visibility?.toLowerCase()})`);
|
|
453
|
+
console.log(` Secrets: ${gh.existingSecrets.length} set, ${gh.missingSecrets.length} missing`);
|
|
454
|
+
if (gh.missingSecrets.length > 0) {
|
|
455
|
+
console.log(` Missing: ${gh.missingSecrets.join(', ')}`);
|
|
456
|
+
}
|
|
457
|
+
const mainBranch = gh.defaultBranch ?? 'main';
|
|
458
|
+
const prot = gh.branchProtection[mainBranch];
|
|
459
|
+
console.log(` Branch prot: ${mainBranch}=${prot?.protected ? '✓ protected' : '✗ unprotected'}`);
|
|
460
|
+
} else if (gh.error) {
|
|
461
|
+
console.log(` GitHub: ${gh.error}`);
|
|
462
|
+
}
|
|
463
|
+
console.log();
|
|
464
|
+
} else {
|
|
465
|
+
process.stdout.write(JSON.stringify(result, null, 2) + '\n');
|
|
466
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# package-wp.sh — build and zip WordPress plugins/themes for upload to a live site.
|
|
3
|
+
#
|
|
4
|
+
# Generalized from the wp-agent-os tools/package-plugins.sh pattern.
|
|
5
|
+
# Reads wp-bootstrap.config.json for package list, or auto-detects from detect-structure.mjs.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# bash .agents/skills/wp-bootstrap/scripts/package-wp.sh [OPTIONS]
|
|
9
|
+
# bash .agents/skills/wp-bootstrap/scripts/package-wp.sh --package=wpaos
|
|
10
|
+
# bash .agents/skills/wp-bootstrap/scripts/package-wp.sh --out=./release
|
|
11
|
+
#
|
|
12
|
+
# Options:
|
|
13
|
+
# --package=<dir> Package only this directory (default: all WP packages)
|
|
14
|
+
# --out=<dir> Output directory (default: dist-plugins/)
|
|
15
|
+
# --no-build Skip build step before packaging
|
|
16
|
+
# --version=<ver> Override version string in zip filename
|
|
17
|
+
# --dry-run Show what would be packaged without doing it
|
|
18
|
+
#
|
|
19
|
+
# Excludes dev-only files from the zip:
|
|
20
|
+
# bin/ tests/ AGENTS.md .pi/ blueprint.json *.md (except readme.txt)
|
|
21
|
+
# composer.json (dev-only — plugins should have no runtime Composer deps)
|
|
22
|
+
# node_modules/ vendor/ .DS_Store
|
|
23
|
+
#
|
|
24
|
+
# After packaging: upload both zips to wp-admin → Plugins → Add New → Upload.
|
|
25
|
+
# Order: wpaos-blocks (or generated plugin) first, then the companion plugin.
|
|
26
|
+
|
|
27
|
+
set -euo pipefail
|
|
28
|
+
root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
29
|
+
cd "$root"
|
|
30
|
+
|
|
31
|
+
PACKAGE_FILTER=""
|
|
32
|
+
OUT_DIR="dist-plugins"
|
|
33
|
+
NO_BUILD=false
|
|
34
|
+
DRY_RUN=false
|
|
35
|
+
VERSION_OVERRIDE=""
|
|
36
|
+
|
|
37
|
+
for arg in "$@"; do
|
|
38
|
+
case "$arg" in
|
|
39
|
+
--package=*) PACKAGE_FILTER="${arg#--package=}" ;;
|
|
40
|
+
--out=*) OUT_DIR="${arg#--out=}" ;;
|
|
41
|
+
--no-build) NO_BUILD=true ;;
|
|
42
|
+
--version=*) VERSION_OVERRIDE="${arg#--version=}" ;;
|
|
43
|
+
--dry-run) DRY_RUN=true ;;
|
|
44
|
+
esac
|
|
45
|
+
done
|
|
46
|
+
|
|
47
|
+
# ── Detect WP packages ────────────────────────────────────────────────────────
|
|
48
|
+
DETECT_SCRIPT="$(dirname "$0")/detect-structure.mjs"
|
|
49
|
+
PACKAGES=()
|
|
50
|
+
|
|
51
|
+
if command -v node >/dev/null 2>&1 && [ -f "$DETECT_SCRIPT" ]; then
|
|
52
|
+
while IFS= read -r line; do
|
|
53
|
+
PACKAGES+=("$line")
|
|
54
|
+
done < <(node "$DETECT_SCRIPT" "$root" | node -e "
|
|
55
|
+
const chunks = [];
|
|
56
|
+
process.stdin.on('data', c => chunks.push(c));
|
|
57
|
+
process.stdin.on('end', () => {
|
|
58
|
+
const r = JSON.parse(chunks.join(''));
|
|
59
|
+
r.wpPackages.forEach(p => console.log(p.path));
|
|
60
|
+
});
|
|
61
|
+
" 2>/dev/null)
|
|
62
|
+
else
|
|
63
|
+
# Fallback: look for directories with Plugin Name: or Theme Name: in a PHP file
|
|
64
|
+
while IFS= read -r dir; do
|
|
65
|
+
[ -n "$dir" ] && PACKAGES+=("$dir")
|
|
66
|
+
done < <(find . -maxdepth 2 -name "*.php" -not -path "*/vendor/*" -not -path "*/node_modules/*" \
|
|
67
|
+
-exec grep -l "Plugin Name:\|Theme Name:" {} \; 2>/dev/null \
|
|
68
|
+
| xargs -I{} dirname {} | sort -u | sed 's|^\./||')
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
[ "${#PACKAGES[@]}" -eq 0 ] && { echo "❌ No WordPress packages found. Run from repo root or specify --package=<dir>."; exit 1; }
|
|
72
|
+
|
|
73
|
+
# Filter if --package specified
|
|
74
|
+
if [ -n "$PACKAGE_FILTER" ]; then
|
|
75
|
+
PACKAGES=("$PACKAGE_FILTER")
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# ── Run build step first ──────────────────────────────────────────────────────
|
|
79
|
+
if ! $NO_BUILD && ! $DRY_RUN; then
|
|
80
|
+
echo "▶ Running build before packaging..."
|
|
81
|
+
CONFIG_FILE="$root/wp-bootstrap.config.json"
|
|
82
|
+
if [ -f "$CONFIG_FILE" ] && command -v node >/dev/null 2>&1; then
|
|
83
|
+
BUILD_CMD=$(node -e "process.stdout.write(require('$CONFIG_FILE').buildCommand ?? '')" 2>/dev/null)
|
|
84
|
+
fi
|
|
85
|
+
BUILD_CMD="${BUILD_CMD:-}"
|
|
86
|
+
|
|
87
|
+
if [ -n "$BUILD_CMD" ]; then
|
|
88
|
+
echo " $BUILD_CMD"
|
|
89
|
+
eval "$BUILD_CMD"
|
|
90
|
+
elif [ -f "package.json" ]; then
|
|
91
|
+
if grep -q '"build"' package.json; then
|
|
92
|
+
PKG_MGR="npm"
|
|
93
|
+
[ -f "pnpm-lock.yaml" ] && PKG_MGR="pnpm"
|
|
94
|
+
$PKG_MGR run build 2>/dev/null || echo " ⚠ build step failed — packaging current files"
|
|
95
|
+
fi
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# ── Package each WP plugin/theme ──────────────────────────────────────────────
|
|
100
|
+
[ -d "$OUT_DIR" ] || mkdir -p "$OUT_DIR"
|
|
101
|
+
|
|
102
|
+
EXCLUDE=(
|
|
103
|
+
-x '*/bin/*'
|
|
104
|
+
-x '*/tests/*'
|
|
105
|
+
-x '*/test/*'
|
|
106
|
+
-x '*/AGENTS.md'
|
|
107
|
+
-x '*/.pi/*'
|
|
108
|
+
-x '*/blueprint.json'
|
|
109
|
+
-x '*/.DS_Store'
|
|
110
|
+
-x '*/node_modules/*'
|
|
111
|
+
-x '*/vendor/*'
|
|
112
|
+
-x '*/.git/*'
|
|
113
|
+
-x '*/coverage/*'
|
|
114
|
+
-x '*/.env'
|
|
115
|
+
-x '*/phpunit.xml'
|
|
116
|
+
-x '*/phpcs.xml*'
|
|
117
|
+
-x '*/phpstan.neon*'
|
|
118
|
+
-x '*/biome.json'
|
|
119
|
+
-x '*/tsconfig.json'
|
|
120
|
+
-x '*/vitest.config*'
|
|
121
|
+
-x '*/package-lock.json'
|
|
122
|
+
-x '*/pnpm-lock.yaml'
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
PACKAGED=()
|
|
126
|
+
|
|
127
|
+
for pkg_dir in "${PACKAGES[@]}"; do
|
|
128
|
+
[ ! -d "$root/$pkg_dir" ] && { echo " ⚠ $pkg_dir not found — skipping"; continue; }
|
|
129
|
+
|
|
130
|
+
# Get version from Plugin Name / Theme file
|
|
131
|
+
VERSION="$VERSION_OVERRIDE"
|
|
132
|
+
if [ -z "$VERSION" ]; then
|
|
133
|
+
# Try plugin header
|
|
134
|
+
MAIN_PHP=$(find "$root/$pkg_dir" -maxdepth 1 -name "*.php" | head -1)
|
|
135
|
+
if [ -n "$MAIN_PHP" ]; then
|
|
136
|
+
VERSION=$(grep -i "Version:" "$MAIN_PHP" | head -1 | sed 's/.*Version: *//' | tr -d '[:space:]') || true
|
|
137
|
+
fi
|
|
138
|
+
# Try style.css (theme)
|
|
139
|
+
if [ -z "$VERSION" ] && [ -f "$root/$pkg_dir/style.css" ]; then
|
|
140
|
+
VERSION=$(grep -i "Version:" "$root/$pkg_dir/style.css" | head -1 | sed 's/.*Version: *//' | tr -d '[:space:]') || true
|
|
141
|
+
fi
|
|
142
|
+
# Try package.json
|
|
143
|
+
if [ -z "$VERSION" ] && [ -f "$root/$pkg_dir/package.json" ] && command -v node >/dev/null 2>&1; then
|
|
144
|
+
VERSION=$(node -e "process.stdout.write(require('$root/$pkg_dir/package.json').version ?? '')" 2>/dev/null) || true
|
|
145
|
+
fi
|
|
146
|
+
VERSION="${VERSION:-unknown}"
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
SLUG="$(basename "$pkg_dir")"
|
|
150
|
+
ZIP_FILE="$OUT_DIR/${SLUG}-${VERSION}.zip"
|
|
151
|
+
|
|
152
|
+
if $DRY_RUN; then
|
|
153
|
+
echo " [dry-run] Would package: $pkg_dir → $ZIP_FILE"
|
|
154
|
+
else
|
|
155
|
+
echo "▶ Packaging $pkg_dir (v$VERSION) → $ZIP_FILE"
|
|
156
|
+
( cd "$root" && zip -rq "$ZIP_FILE" "$pkg_dir" "${EXCLUDE[@]}" )
|
|
157
|
+
SIZE=$(du -sh "$ZIP_FILE" | cut -f1)
|
|
158
|
+
echo " ✓ $ZIP_FILE ($SIZE)"
|
|
159
|
+
PACKAGED+=("$ZIP_FILE")
|
|
160
|
+
fi
|
|
161
|
+
done
|
|
162
|
+
|
|
163
|
+
echo ""
|
|
164
|
+
if $DRY_RUN; then
|
|
165
|
+
echo "Dry run complete. No files created."
|
|
166
|
+
else
|
|
167
|
+
echo "Packaged ${#PACKAGED[@]} artifact(s) in $OUT_DIR/:"
|
|
168
|
+
ls -lh "$OUT_DIR/"
|
|
169
|
+
echo ""
|
|
170
|
+
echo "Upload order: generated/blocks plugin first, then companion plugin."
|
|
171
|
+
echo "wp-admin → Plugins → Add New → Upload Plugin"
|
|
172
|
+
echo "Or with WP-CLI: wp plugin install <file.zip> --activate"
|
|
173
|
+
fi
|