wordpress-agent-kit 0.2.1 → 0.2.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.
- package/.github/workflows/ci.yml +44 -0
- package/.husky/pre-commit +7 -0
- package/CLI_REVIEW.md +250 -0
- package/README.md +124 -51
- package/biome.json +39 -0
- package/dist/cli.js +75 -4
- package/dist/commands/install.js +43 -10
- package/dist/commands/run-playground.js +59 -14
- package/dist/commands/setup.js +222 -163
- package/dist/commands/sync-skills.js +33 -60
- package/dist/commands/upgrade.js +182 -0
- package/dist/lib/api.js +456 -0
- package/dist/lib/triage-mapper.js +18 -20
- package/dist/utils/exit-codes.js +60 -0
- package/dist/utils/output.js +96 -0
- package/dist/utils/paths.js +1 -1
- package/dist/utils/run.js +1 -1
- package/kit-learnings.md +192 -0
- package/package.json +7 -2
package/dist/commands/install.js
CHANGED
|
@@ -1,27 +1,60 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
1
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { installKitApi, } from '../lib/api.js';
|
|
4
|
+
import { OutputFormatter, createFormatter } from '../utils/output.js';
|
|
5
|
+
function isDryRunResult(result) {
|
|
6
|
+
return result.success && 'wouldExecute' in (result.data || {});
|
|
7
|
+
}
|
|
8
|
+
function isRegularResult(result) {
|
|
9
|
+
return result.success && !('wouldExecute' in (result.data || {}));
|
|
10
|
+
}
|
|
4
11
|
/**
|
|
5
12
|
* Command to install the WordPress Agent Kit into a target directory.
|
|
6
13
|
* Takes an optional directory argument, defaulting to the current working directory.
|
|
14
|
+
* Supports --json, --quiet, --ndjson, --dry-run global flags.
|
|
7
15
|
*/
|
|
8
16
|
export const installCommand = new Command('install')
|
|
9
17
|
.description('Install the WordPress Agent Kit into a target directory')
|
|
10
18
|
.argument('[dir]', 'Target directory to install into', process.cwd())
|
|
11
19
|
.option('--platform <platform>', 'Target platform (github, cursor, claude, agent, pi)', 'github')
|
|
12
|
-
.
|
|
20
|
+
.option('--force', 'Overwrite existing installation', false)
|
|
21
|
+
.action(async (dir, options, command) => {
|
|
22
|
+
const globalOpts = command.parent?.opts() || {};
|
|
13
23
|
const platform = options.platform;
|
|
14
24
|
const validPlatforms = ['github', 'cursor', 'claude', 'agent', 'pi'];
|
|
15
25
|
if (!validPlatforms.includes(platform)) {
|
|
16
|
-
|
|
17
|
-
|
|
26
|
+
const formatter = createFormatter(globalOpts, 'install', '0.0.0');
|
|
27
|
+
const result = formatter.fail({
|
|
28
|
+
code: 'INVALID_PLATFORM',
|
|
29
|
+
message: `Invalid platform: ${platform}. Valid options: ${validPlatforms.join(', ')}`,
|
|
30
|
+
exitCode: 2,
|
|
31
|
+
});
|
|
32
|
+
process.exit(OutputFormatter.getExitCode(result));
|
|
18
33
|
}
|
|
19
34
|
const targetDir = path.resolve(dir);
|
|
20
|
-
|
|
21
|
-
|
|
35
|
+
const installOptions = {
|
|
36
|
+
targetDir,
|
|
37
|
+
platform: platform,
|
|
38
|
+
force: options.force,
|
|
39
|
+
dryRun: globalOpts.dryRun,
|
|
40
|
+
};
|
|
41
|
+
const result = await installKitApi(installOptions);
|
|
42
|
+
if (globalOpts.json || globalOpts.ndjson || globalOpts.quiet) {
|
|
43
|
+
process.exit(OutputFormatter.getExitCode(result));
|
|
44
|
+
}
|
|
45
|
+
// Human-readable output
|
|
46
|
+
if (isRegularResult(result)) {
|
|
47
|
+
const data = result.data;
|
|
48
|
+
console.log(`✓ Installed WordPress Agent Kit (${platform}) to ${targetDir}`);
|
|
49
|
+
console.log(` Files: ${data.filesCreated.length} created, ${data.filesSkipped.length} skipped`);
|
|
50
|
+
console.log(` Duration: ${data.durationMs}ms`);
|
|
51
|
+
}
|
|
52
|
+
else if (isDryRunResult(result)) {
|
|
53
|
+
// Dry-run result - just show summary
|
|
54
|
+
console.log(`✓ Dry-run: ${result.data.summary.filesCreated.length} files would be created`);
|
|
22
55
|
}
|
|
23
|
-
|
|
24
|
-
console.error(error
|
|
25
|
-
process.exit(1);
|
|
56
|
+
else {
|
|
57
|
+
console.error(`✗ Installation failed: ${result.error?.message}`);
|
|
26
58
|
}
|
|
59
|
+
process.exit(result.success ? 0 : 1);
|
|
27
60
|
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import path from 'node:path';
|
|
3
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { ExitCode } from '../utils/exit-codes.js';
|
|
5
|
+
import { OutputFormatter, createFormatter } from '../utils/output.js';
|
|
4
6
|
import { PACKAGE_ROOT } from '../utils/paths.js';
|
|
5
7
|
/**
|
|
6
8
|
* Command to run a local WordPress Playground instance.
|
|
@@ -8,21 +10,64 @@ import { PACKAGE_ROOT } from '../utils/paths.js';
|
|
|
8
10
|
*/
|
|
9
11
|
export const runPlaygroundCommand = new Command('playground')
|
|
10
12
|
.description('Run local WordPress Playground')
|
|
11
|
-
.
|
|
12
|
-
|
|
13
|
+
.option('--port <port>', 'Port to run on', '9400')
|
|
14
|
+
.option('--no-auto-mount', 'Disable auto-mount of current directory')
|
|
15
|
+
.action(async (options, command) => {
|
|
16
|
+
const globalOpts = command.parent?.opts() || {};
|
|
17
|
+
const formatter = createFormatter(globalOpts, 'playground', '0.0.0');
|
|
18
|
+
const port = process.env.PORT || options.port;
|
|
13
19
|
const blueprintPath = path.join(PACKAGE_ROOT, 'playground', 'blueprint.json');
|
|
20
|
+
if (!globalOpts.json && !globalOpts.quiet) {
|
|
21
|
+
console.log(`Starting WordPress Playground on port ${port}...`);
|
|
22
|
+
console.log(`Blueprint: ${blueprintPath}`);
|
|
23
|
+
}
|
|
14
24
|
const args = [
|
|
15
25
|
'@wp-playground/cli@latest',
|
|
16
26
|
'server',
|
|
17
|
-
'--auto-mount',
|
|
27
|
+
options.autoMount ? '--auto-mount' : '',
|
|
18
28
|
`--port=${port}`,
|
|
19
|
-
`--blueprint=${blueprintPath}
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
`--blueprint=${blueprintPath}`,
|
|
30
|
+
].filter(Boolean);
|
|
31
|
+
if (globalOpts.dryRun) {
|
|
32
|
+
const dryRunResult = formatter.success({
|
|
33
|
+
wouldExecute: true,
|
|
34
|
+
actions: [
|
|
35
|
+
{
|
|
36
|
+
type: 'create',
|
|
37
|
+
target: `localhost:${port}`,
|
|
38
|
+
description: 'Start WP Playground server with blueprint',
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
summary: { port: Number(port), blueprint: blueprintPath },
|
|
42
|
+
});
|
|
43
|
+
process.exit(OutputFormatter.getExitCode(dryRunResult));
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const result = spawnSync('npx', args, {
|
|
47
|
+
cwd: PACKAGE_ROOT,
|
|
48
|
+
stdio: 'inherit',
|
|
49
|
+
shell: process.platform === 'win32',
|
|
50
|
+
});
|
|
51
|
+
if (result.status !== 0) {
|
|
52
|
+
const errorResult = formatter.fail({
|
|
53
|
+
code: 'PLAYGROUND_FAILED',
|
|
54
|
+
message: `Playground exited with code ${result.status}`,
|
|
55
|
+
exitCode: ExitCode.ERROR,
|
|
56
|
+
});
|
|
57
|
+
process.exit(OutputFormatter.getExitCode(errorResult));
|
|
58
|
+
}
|
|
59
|
+
if (globalOpts.json) {
|
|
60
|
+
const successResult = formatter.success({ port: Number(port), status: 'completed' });
|
|
61
|
+
process.exit(OutputFormatter.getExitCode(successResult));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
66
|
+
const errorResult = formatter.fail({
|
|
67
|
+
code: 'PLAYGROUND_ERROR',
|
|
68
|
+
message: errorMessage,
|
|
69
|
+
exitCode: ExitCode.ERROR,
|
|
70
|
+
});
|
|
71
|
+
process.exit(OutputFormatter.getExitCode(errorResult));
|
|
72
|
+
}
|
|
28
73
|
});
|
package/dist/commands/setup.js
CHANGED
|
@@ -1,164 +1,217 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import * as p from '@clack/prompts';
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
3
2
|
import fs from 'node:fs';
|
|
4
3
|
import path from 'node:path';
|
|
5
|
-
import
|
|
4
|
+
import * as p from '@clack/prompts';
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
import { configureAgentsMdApi, installKitApi, } from '../lib/api.js';
|
|
7
|
+
import { PLATFORM_FOLDERS } from '../lib/installer.js';
|
|
8
|
+
import { formatDetectionResults, hasConfidentDetection, mapProjectType, mapTechStack, } from '../lib/triage-mapper.js';
|
|
9
|
+
import { ExitCode } from '../utils/exit-codes.js';
|
|
10
|
+
import { OutputFormatter, createFormatter } from '../utils/output.js';
|
|
6
11
|
import { PACKAGE_ROOT } from '../utils/paths.js';
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
const VALID_PROJECT_TYPES = ['plugin', 'theme', 'block-theme', 'site', 'blocks', 'other'];
|
|
13
|
+
function isValidProjectType(type) {
|
|
14
|
+
return VALID_PROJECT_TYPES.includes(type);
|
|
15
|
+
}
|
|
16
|
+
function isDryRunResult(result) {
|
|
17
|
+
return result.success && 'wouldExecute' in (result.data || {});
|
|
18
|
+
}
|
|
19
|
+
function isRegularResult(result) {
|
|
20
|
+
return result.success && !('wouldExecute' in (result.data || {}));
|
|
21
|
+
}
|
|
9
22
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
23
|
+
* Interactive/Headless setup for WordPress Agent Kit.
|
|
24
|
+
* Supports both interactive prompts and --auto/--project-type/--tech-stack for headless use.
|
|
12
25
|
*/
|
|
13
26
|
export const setupCommand = new Command('setup')
|
|
14
|
-
.description('Interactive setup for WordPress Agent Kit')
|
|
27
|
+
.description('Interactive or headless setup for WordPress Agent Kit')
|
|
15
28
|
.argument('[dir]', 'Target directory', process.cwd())
|
|
16
29
|
.option('--reset', 'Reset and overwrite existing configuration')
|
|
17
30
|
.option('--platform <platform>', 'Target platform (github, cursor, claude, agent, pi)', 'github')
|
|
18
|
-
.
|
|
31
|
+
.option('--auto', 'Run triage, apply detected values, skip prompts', false)
|
|
32
|
+
.option('--project-type <type>', 'Project type: plugin, theme, block-theme, site, blocks, other')
|
|
33
|
+
.option('--tech-stack <list>', 'Comma-separated tech: gutenberg,interactivity,rest-api,wpcli,composer,phpstan,npm,playground')
|
|
34
|
+
.option('--package-manager <pm>', 'Package manager: npm, pnpm, yarn')
|
|
35
|
+
.option('-y, --yes', 'Accept all confirmations (requires --project-type in headless mode)')
|
|
36
|
+
.action(async (dir, options, command) => {
|
|
37
|
+
const globalOpts = command.parent?.opts() || {};
|
|
19
38
|
const platform = options.platform;
|
|
20
39
|
const validPlatforms = ['github', 'cursor', 'claude', 'agent', 'pi'];
|
|
21
40
|
if (!validPlatforms.includes(platform)) {
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
const formatter = createFormatter(globalOpts, 'setup', '0.0.0');
|
|
42
|
+
const result = formatter.fail({
|
|
43
|
+
code: 'INVALID_PLATFORM',
|
|
44
|
+
message: `Invalid platform: ${platform}. Valid options: ${validPlatforms.join(', ')}`,
|
|
45
|
+
exitCode: ExitCode.INVALID_ARGS,
|
|
46
|
+
});
|
|
47
|
+
process.exit(OutputFormatter.getExitCode(result));
|
|
24
48
|
}
|
|
25
|
-
const
|
|
26
|
-
console.clear();
|
|
27
|
-
p.intro('WordPress Agent Kit Setup');
|
|
49
|
+
const formatter = createFormatter(globalOpts, 'setup', '0.0.0');
|
|
28
50
|
const targetDir = path.resolve(dir);
|
|
29
|
-
|
|
51
|
+
const platformFolder = PLATFORM_FOLDERS[platform];
|
|
52
|
+
const isHeadless = options.auto || options.projectType || globalOpts.json || globalOpts.quiet;
|
|
53
|
+
// Create target directory if needed
|
|
30
54
|
if (!fs.existsSync(targetDir)) {
|
|
31
|
-
|
|
32
|
-
message: `Target directory doesn't exist: ${targetDir}\nCreate it?`,
|
|
33
|
-
initialValue: true,
|
|
34
|
-
});
|
|
35
|
-
if (p.isCancel(shouldCreate) || !shouldCreate) {
|
|
36
|
-
p.cancel('Setup cancelled.');
|
|
37
|
-
process.exit(0);
|
|
38
|
-
}
|
|
39
|
-
try {
|
|
55
|
+
if (isHeadless) {
|
|
40
56
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
41
|
-
p.log.success(`Created directory: ${targetDir}`);
|
|
42
57
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
else {
|
|
59
|
+
const shouldCreate = await p.confirm({
|
|
60
|
+
message: `Target directory doesn't exist: ${targetDir}\nCreate it?`,
|
|
61
|
+
initialValue: true,
|
|
62
|
+
});
|
|
63
|
+
if (p.isCancel(shouldCreate) || !shouldCreate) {
|
|
64
|
+
const result = formatter.fail({
|
|
65
|
+
code: 'CANCELLED',
|
|
66
|
+
message: 'Setup cancelled.',
|
|
67
|
+
exitCode: ExitCode.CANCELLED,
|
|
68
|
+
});
|
|
69
|
+
process.exit(OutputFormatter.getExitCode(result));
|
|
70
|
+
}
|
|
71
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
46
72
|
}
|
|
47
73
|
}
|
|
48
|
-
|
|
49
|
-
// Check if kit is already installed
|
|
74
|
+
// Install kit if not present or --reset
|
|
50
75
|
const agentsPath = path.join(targetDir, 'AGENTS.md');
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
p.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
s.stop('Kit files reset.');
|
|
67
|
-
}
|
|
68
|
-
catch (err) {
|
|
69
|
-
s.stop('Reset failed.');
|
|
70
|
-
console.error(err.message);
|
|
71
|
-
process.exit(1);
|
|
76
|
+
const needsInstall = options.reset || !fs.existsSync(agentsPath);
|
|
77
|
+
if (needsInstall) {
|
|
78
|
+
if (!isHeadless) {
|
|
79
|
+
const shouldInstall = await p.confirm({
|
|
80
|
+
message: 'Kit not found in target repo. Install it first?',
|
|
81
|
+
initialValue: true,
|
|
82
|
+
});
|
|
83
|
+
if (p.isCancel(shouldInstall) || !shouldInstall) {
|
|
84
|
+
const result = formatter.fail({
|
|
85
|
+
code: 'CANCELLED',
|
|
86
|
+
message: 'Setup cancelled.',
|
|
87
|
+
exitCode: ExitCode.CANCELLED,
|
|
88
|
+
});
|
|
89
|
+
process.exit(OutputFormatter.getExitCode(result));
|
|
90
|
+
}
|
|
72
91
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
92
|
+
const installResult = await installKitApi({
|
|
93
|
+
targetDir,
|
|
94
|
+
platform,
|
|
95
|
+
force: options.reset,
|
|
96
|
+
dryRun: globalOpts.dryRun,
|
|
78
97
|
});
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
process.exit(0);
|
|
98
|
+
if (!installResult.success) {
|
|
99
|
+
process.exit(OutputFormatter.getExitCode(installResult));
|
|
82
100
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
await installKit(targetDir, platform);
|
|
87
|
-
s.stop('Kit installed successfully.');
|
|
101
|
+
if (globalOpts.json || globalOpts.quiet) {
|
|
102
|
+
// Continue silently in headless mode
|
|
88
103
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
console.error(err.message);
|
|
92
|
-
process.exit(1);
|
|
104
|
+
else {
|
|
105
|
+
console.log('✓ Kit installed');
|
|
93
106
|
}
|
|
94
107
|
}
|
|
95
|
-
// Run project triage
|
|
108
|
+
// Run project triage
|
|
96
109
|
let triageResult = null;
|
|
97
110
|
let detectedType = null;
|
|
98
111
|
let detectedTech = [];
|
|
99
|
-
|
|
112
|
+
let detectedPackageManager = 'npm/pnpm';
|
|
100
113
|
const triageScriptPaths = [
|
|
101
114
|
path.join(targetDir, platformFolder, 'skills/wp-project-triage/scripts/detect_wp_project.mjs'),
|
|
102
|
-
path.join(
|
|
103
|
-
path.resolve(PACKAGE_ROOT, 'vendor/wp-agent-skills/skills/wp-project-triage/scripts/detect_wp_project.mjs'),
|
|
115
|
+
path.join(PACKAGE_ROOT, 'vendor/wp-agent-skills/skills/wp-project-triage/scripts/detect_wp_project.mjs'),
|
|
104
116
|
];
|
|
105
|
-
const triageScriptPath = triageScriptPaths.find(p => fs.existsSync(p));
|
|
117
|
+
const triageScriptPath = triageScriptPaths.find((p) => fs.existsSync(p));
|
|
106
118
|
if (triageScriptPath) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const result = spawnSync('node', [triageScriptPath], {
|
|
111
|
-
cwd: targetDir,
|
|
112
|
-
encoding: 'utf-8',
|
|
113
|
-
});
|
|
114
|
-
if (result.status === 0 && result.stdout) {
|
|
115
|
-
triageResult = JSON.parse(result.stdout.trim());
|
|
116
|
-
detectedType = mapProjectType(triageResult.project?.primary);
|
|
117
|
-
detectedTech = mapTechStack(triageResult);
|
|
118
|
-
}
|
|
119
|
-
s.stop('Project analyzed.');
|
|
120
|
-
}
|
|
121
|
-
catch (err) {
|
|
122
|
-
s.stop('Auto-detection unavailable.');
|
|
123
|
-
p.log.warn('Could not run project triage. Proceeding with manual setup.');
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
else {
|
|
127
|
-
p.log.info('Project triage not available yet. Using manual setup.');
|
|
128
|
-
}
|
|
129
|
-
let detectedPackageManager = 'npm/pnpm';
|
|
130
|
-
if (triageResult && triageResult.tooling?.node?.packageManager) {
|
|
131
|
-
detectedPackageManager = triageResult.tooling.node.packageManager;
|
|
132
|
-
}
|
|
133
|
-
let useDetectedValues = false;
|
|
134
|
-
if (triageResult && hasConfidentDetection(detectedType, detectedTech)) {
|
|
135
|
-
p.note(formatDetectionResults(detectedType, detectedTech, triageResult), 'Auto-Detection Results');
|
|
136
|
-
const confirmDetection = await p.confirm({
|
|
137
|
-
message: 'Use these detected values?',
|
|
138
|
-
initialValue: true,
|
|
119
|
+
const result = spawnSync('node', [triageScriptPath], {
|
|
120
|
+
cwd: targetDir,
|
|
121
|
+
encoding: 'utf-8',
|
|
139
122
|
});
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
|
|
123
|
+
if (result.status === 0 && result.stdout) {
|
|
124
|
+
triageResult = JSON.parse(result.stdout.trim());
|
|
125
|
+
detectedType = mapProjectType(triageResult.project?.primary ?? '');
|
|
126
|
+
detectedTech = mapTechStack(triageResult);
|
|
127
|
+
if (triageResult.tooling?.node?.packageManager) {
|
|
128
|
+
detectedPackageManager = triageResult.tooling.node.packageManager;
|
|
129
|
+
}
|
|
143
130
|
}
|
|
144
|
-
useDetectedValues = confirmDetection;
|
|
145
131
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
132
|
+
// Determine project config
|
|
133
|
+
let projectConfig;
|
|
134
|
+
if (options.auto && triageResult) {
|
|
135
|
+
// Auto mode: use detected values
|
|
136
|
+
if (!detectedType || detectedType === 'other') {
|
|
137
|
+
const result = formatter.fail({
|
|
138
|
+
code: 'AUTO_DETECTION_FAILED',
|
|
139
|
+
message: 'Auto-detection could not determine project type confidently. Use --project-type explicitly.',
|
|
140
|
+
exitCode: ExitCode.VALIDATION_ERROR,
|
|
141
|
+
});
|
|
142
|
+
process.exit(OutputFormatter.getExitCode(result));
|
|
149
143
|
}
|
|
150
|
-
|
|
151
|
-
let projectInfo;
|
|
152
|
-
if (useDetectedValues) {
|
|
153
|
-
projectInfo = {
|
|
144
|
+
projectConfig = {
|
|
154
145
|
projectType: detectedType,
|
|
155
146
|
techStack: detectedTech,
|
|
147
|
+
packageManager: detectedPackageManager,
|
|
156
148
|
};
|
|
157
|
-
|
|
149
|
+
if (!globalOpts.json && !globalOpts.quiet) {
|
|
150
|
+
console.log(`Auto-detected: ${detectedType} (${detectedTech.join(', ')})`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else if (options.projectType) {
|
|
154
|
+
// Explicit project type provided (headless)
|
|
155
|
+
if (!isValidProjectType(options.projectType)) {
|
|
156
|
+
const result = formatter.fail({
|
|
157
|
+
code: 'INVALID_PROJECT_TYPE',
|
|
158
|
+
message: `Invalid project type: ${options.projectType}. Valid: ${VALID_PROJECT_TYPES.join(', ')}`,
|
|
159
|
+
exitCode: ExitCode.INVALID_ARGS,
|
|
160
|
+
});
|
|
161
|
+
process.exit(OutputFormatter.getExitCode(result));
|
|
162
|
+
}
|
|
163
|
+
projectConfig = {
|
|
164
|
+
projectType: options.projectType,
|
|
165
|
+
techStack: options.techStack
|
|
166
|
+
? options.techStack.split(',').map((s) => s.trim())
|
|
167
|
+
: detectedTech,
|
|
168
|
+
packageManager: options.packageManager || detectedPackageManager,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
else if (isHeadless) {
|
|
172
|
+
// Headless without enough info
|
|
173
|
+
const result = formatter.fail({
|
|
174
|
+
code: 'MISSING_CONFIG',
|
|
175
|
+
message: 'Headless mode requires --project-type (and optionally --tech-stack). Use --auto for auto-detection.',
|
|
176
|
+
exitCode: ExitCode.INVALID_ARGS,
|
|
177
|
+
});
|
|
178
|
+
process.exit(OutputFormatter.getExitCode(result));
|
|
158
179
|
}
|
|
159
180
|
else {
|
|
160
|
-
|
|
161
|
-
|
|
181
|
+
// Interactive mode - prompt user
|
|
182
|
+
if (!globalOpts.json && !globalOpts.quiet) {
|
|
183
|
+
console.clear();
|
|
184
|
+
p.intro('WordPress Agent Kit Setup');
|
|
185
|
+
console.log(`Setting up kit in: ${targetDir} (platform: ${platform})`);
|
|
186
|
+
}
|
|
187
|
+
let useDetected = false;
|
|
188
|
+
if (triageResult && hasConfidentDetection(detectedType)) {
|
|
189
|
+
if (!globalOpts.json && !globalOpts.quiet) {
|
|
190
|
+
p.note(formatDetectionResults(detectedType, detectedTech), 'Auto-Detection Results');
|
|
191
|
+
const confirm = await p.confirm({
|
|
192
|
+
message: 'Use these detected values?',
|
|
193
|
+
initialValue: true,
|
|
194
|
+
});
|
|
195
|
+
if (p.isCancel(confirm))
|
|
196
|
+
process.exit(ExitCode.CANCELLED);
|
|
197
|
+
useDetected = confirm;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else if (triageResult && (detectedType || detectedTech.length > 0)) {
|
|
201
|
+
if (!globalOpts.json && !globalOpts.quiet) {
|
|
202
|
+
p.note(formatDetectionResults(detectedType, detectedTech), 'Partial Detection (used as defaults)');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (useDetected) {
|
|
206
|
+
projectConfig = {
|
|
207
|
+
projectType: detectedType,
|
|
208
|
+
techStack: detectedTech,
|
|
209
|
+
packageManager: detectedPackageManager,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
// Interactive prompts
|
|
214
|
+
const projectTypePrompt = p.select({
|
|
162
215
|
message: 'What type of WordPress project is this?',
|
|
163
216
|
options: [
|
|
164
217
|
{ value: 'plugin', label: 'Plugin' },
|
|
@@ -170,11 +223,15 @@ export const setupCommand = new Command('setup')
|
|
|
170
223
|
{ value: 'unsure', label: "I'm not sure" },
|
|
171
224
|
],
|
|
172
225
|
initialValue: detectedType || undefined,
|
|
173
|
-
})
|
|
174
|
-
|
|
226
|
+
});
|
|
227
|
+
const techStackPrompt = p.multiselect({
|
|
175
228
|
message: 'Select technologies (or skip if unsure):',
|
|
176
229
|
options: [
|
|
177
|
-
{
|
|
230
|
+
{
|
|
231
|
+
value: 'gutenberg',
|
|
232
|
+
label: 'Gutenberg Blocks',
|
|
233
|
+
hint: 'block.json, @wordpress/blocks',
|
|
234
|
+
},
|
|
178
235
|
{ value: 'interactivity', label: 'Interactivity API', hint: 'data-wp-* directives' },
|
|
179
236
|
{ value: 'rest-api', label: 'REST API', hint: 'Custom endpoints' },
|
|
180
237
|
{ value: 'wpcli', label: 'WP-CLI', hint: 'Custom commands' },
|
|
@@ -185,53 +242,55 @@ export const setupCommand = new Command('setup')
|
|
|
185
242
|
],
|
|
186
243
|
initialValues: detectedTech.length > 0 ? detectedTech : undefined,
|
|
187
244
|
required: false,
|
|
188
|
-
})
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (projectInfo.projectType === 'unsure') {
|
|
196
|
-
projectInfo.projectType = 'other';
|
|
197
|
-
p.log.info('Using "other" as project type. You can adjust AGENTS.md later.');
|
|
245
|
+
});
|
|
246
|
+
const { projectType, techStack } = await p.group({ projectType: () => projectTypePrompt, techStack: () => techStackPrompt }, { onCancel: () => process.exit(ExitCode.CANCELLED) });
|
|
247
|
+
projectConfig = {
|
|
248
|
+
projectType: (projectType === 'unsure' ? 'other' : projectType),
|
|
249
|
+
techStack,
|
|
250
|
+
packageManager: detectedPackageManager,
|
|
251
|
+
};
|
|
198
252
|
}
|
|
199
253
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
254
|
+
// Configure AGENTS.md
|
|
255
|
+
const configureResult = await configureAgentsMdApi({
|
|
256
|
+
targetDir,
|
|
257
|
+
platform,
|
|
258
|
+
config: projectConfig,
|
|
259
|
+
dryRun: globalOpts.dryRun,
|
|
203
260
|
});
|
|
204
|
-
if (
|
|
205
|
-
|
|
206
|
-
process.exit(0);
|
|
261
|
+
if (!configureResult.success) {
|
|
262
|
+
process.exit(OutputFormatter.getExitCode(configureResult));
|
|
207
263
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
264
|
+
// Success output
|
|
265
|
+
if (globalOpts.json || globalOpts.quiet) {
|
|
266
|
+
let modified = [];
|
|
267
|
+
let skipped = [];
|
|
268
|
+
if (isRegularResult(configureResult)) {
|
|
269
|
+
modified = configureResult.data.modified;
|
|
270
|
+
skipped = configureResult.data.skipped;
|
|
214
271
|
}
|
|
215
|
-
|
|
216
|
-
|
|
272
|
+
else if (isDryRunResult(configureResult)) {
|
|
273
|
+
// Dry-run
|
|
274
|
+
modified = configureResult.data.summary.modified;
|
|
275
|
+
skipped = configureResult.data.summary.skipped;
|
|
217
276
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
277
|
+
const result = formatter.success({
|
|
278
|
+
targetDir,
|
|
279
|
+
platform,
|
|
280
|
+
config: projectConfig,
|
|
281
|
+
modified,
|
|
282
|
+
skipped,
|
|
224
283
|
});
|
|
225
|
-
|
|
226
|
-
p.note(`Edit: ${platformInstructionsPath}\n\nAdd your project-specific:\n- Coding standards\n- Git workflow\n- Testing procedures\n- Deployment steps`, 'Workflow Instructions');
|
|
227
|
-
}
|
|
284
|
+
process.exit(OutputFormatter.getExitCode(result));
|
|
228
285
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
286
|
+
// Human output
|
|
287
|
+
console.log('✓ Setup complete');
|
|
288
|
+
console.log(` Project: ${projectConfig.projectType}`);
|
|
289
|
+
console.log(` Tech stack: ${projectConfig.techStack.join(', ') || 'none'}`);
|
|
290
|
+
console.log(` Package manager: ${projectConfig.packageManager}`);
|
|
291
|
+
console.log('\nNext steps:');
|
|
292
|
+
console.log(` 1. Review ${path.join(targetDir, 'AGENTS.md')}`);
|
|
293
|
+
console.log(` 2. Customize ${path.join(targetDir, platformFolder, 'prompts/')}`);
|
|
294
|
+
console.log(` 3. Run triage: node ${path.join(targetDir, platformFolder, 'skills/wp-project-triage/scripts/detect_wp_project.mjs')}`);
|
|
295
|
+
process.exit(0);
|
|
237
296
|
});
|