@hubspot/cli 7.7.0-experimental.0 → 7.7.0-experimental.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/mcp-server/tools/GenerateAppComponentTool.d.ts +2 -2
- package/mcp-server/tools/GenerateAppComponentTool.js +45 -45
- package/mcp-server/tools/HubSpotCLIHelper.d.ts +8 -0
- package/mcp-server/tools/HubSpotCLIHelper.js +55 -19
- package/mcp-server/tools/UploadProjectTool.d.ts +2 -2
- package/mcp-server/tools/UploadProjectTool.js +67 -50
- package/mcp-server/tools/ValidateProjectTool.d.ts +3 -3
- package/mcp-server/tools/ValidateProjectTool.js +126 -105
- package/package.json +1 -1
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { MCPTool } from
|
|
2
|
-
import { z } from
|
|
1
|
+
import { MCPTool } from 'mcp-framework';
|
|
2
|
+
import { z } from 'zod';
|
|
3
3
|
class GenerateAppComponentTool extends MCPTool {
|
|
4
|
-
name =
|
|
5
|
-
description =
|
|
4
|
+
name = 'generateAppComponent';
|
|
5
|
+
description = 'Generates an app component (top-level) with proper folder structure and hsmeta.json configuration file. Apps provide OAuth authentication that sub-components depend on.';
|
|
6
6
|
schema = {
|
|
7
7
|
appName: {
|
|
8
8
|
type: z.string(),
|
|
9
|
-
description:
|
|
9
|
+
description: 'Name of the app (used for file naming and default display name)',
|
|
10
10
|
},
|
|
11
11
|
uid: {
|
|
12
12
|
type: z.string(),
|
|
13
|
-
description:
|
|
13
|
+
description: 'Unique identifier for the app component (must be unique per-project)',
|
|
14
14
|
},
|
|
15
15
|
description: {
|
|
16
16
|
type: z.string().optional(),
|
|
@@ -18,11 +18,11 @@ class GenerateAppComponentTool extends MCPTool {
|
|
|
18
18
|
},
|
|
19
19
|
displayName: {
|
|
20
20
|
type: z.string().optional(),
|
|
21
|
-
description:
|
|
21
|
+
description: 'Display name of the app. Defaults to the appName',
|
|
22
22
|
},
|
|
23
23
|
distribution: {
|
|
24
24
|
type: z.string().optional(),
|
|
25
|
-
description: "Distribution
|
|
25
|
+
description: "Distribution type (one of 'marketplace' or 'private'). Defaults to 'marketplace'",
|
|
26
26
|
},
|
|
27
27
|
supportEmail: {
|
|
28
28
|
type: z.string().optional(),
|
|
@@ -46,11 +46,11 @@ class GenerateAppComponentTool extends MCPTool {
|
|
|
46
46
|
},
|
|
47
47
|
requiredScopes: {
|
|
48
48
|
type: z.array(z.string()).optional(),
|
|
49
|
-
description:
|
|
49
|
+
description: 'Required OAuth scopes. Consider what sub-components you plan to add. Defaults to basic CRM contact scopes',
|
|
50
50
|
},
|
|
51
51
|
optionalScopes: {
|
|
52
52
|
type: z.array(z.string()).optional(),
|
|
53
|
-
description:
|
|
53
|
+
description: 'Optional OAuth scopes. Defaults to empty array',
|
|
54
54
|
},
|
|
55
55
|
permittedFetchUrls: {
|
|
56
56
|
type: z.array(z.string()).optional(),
|
|
@@ -62,23 +62,23 @@ class GenerateAppComponentTool extends MCPTool {
|
|
|
62
62
|
},
|
|
63
63
|
};
|
|
64
64
|
async execute(input) {
|
|
65
|
-
const { appName, uid, description =
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
], optionalScopes = [], permittedFetchUrls = [
|
|
65
|
+
const { appName, uid, description = 'A public app', displayName = appName, distribution = 'marketplace', supportEmail = 'support@example.com', documentationUrl = 'https://example.com/docs', supportUrl = 'https://example.com/support', supportPhone = '+18005555555', redirectUrls = ['http://localhost:3000/oauth-callback'], requiredScopes = [
|
|
66
|
+
'crm.objects.contacts.read',
|
|
67
|
+
'crm.objects.contacts.write',
|
|
68
|
+
], optionalScopes = [], permittedFetchUrls = ['https://api.example.com'], plannedFeatures = [], } = input;
|
|
69
69
|
// Generate the sanitized app name for file naming
|
|
70
|
-
const sanitizedAppName = appName.toLowerCase().replace(/\s+/g,
|
|
70
|
+
const sanitizedAppName = appName.toLowerCase().replace(/\s+/g, '-');
|
|
71
71
|
const fileName = `${sanitizedAppName}-hsmeta.json`;
|
|
72
|
-
const folderPath =
|
|
72
|
+
const folderPath = 'src/app';
|
|
73
73
|
const appConfig = {
|
|
74
74
|
uid: uid,
|
|
75
|
-
type:
|
|
75
|
+
type: 'app',
|
|
76
76
|
config: {
|
|
77
77
|
description: description,
|
|
78
78
|
name: displayName,
|
|
79
79
|
distribution: distribution,
|
|
80
80
|
auth: {
|
|
81
|
-
type:
|
|
81
|
+
type: 'oauth',
|
|
82
82
|
redirectUrls: redirectUrls,
|
|
83
83
|
requiredScopes: requiredScopes,
|
|
84
84
|
optionalScopes: optionalScopes,
|
|
@@ -108,8 +108,8 @@ class GenerateAppComponentTool extends MCPTool {
|
|
|
108
108
|
fileName: fileName,
|
|
109
109
|
fullPath: `${folderPath}/${fileName}`,
|
|
110
110
|
architecture: {
|
|
111
|
-
componentType:
|
|
112
|
-
role:
|
|
111
|
+
componentType: 'top-level',
|
|
112
|
+
role: 'authentication-provider',
|
|
113
113
|
singularComponent: true,
|
|
114
114
|
dependents: "All sub-components (cards, functions, settings, etc.) will depend on this app's authentication",
|
|
115
115
|
},
|
|
@@ -121,8 +121,8 @@ class GenerateAppComponentTool extends MCPTool {
|
|
|
121
121
|
`This is a TOP-LEVEL component that provides OAuth authentication`,
|
|
122
122
|
`Sub-components (features) will live within ${folderPath}/ and depend on this app's authentication`,
|
|
123
123
|
`The app component uid: ${uid} must be unique within your project`,
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
'Consider what sub-components you plan to add and ensure sufficient OAuth scopes',
|
|
125
|
+
'Typically there is one app component per project (singular component)',
|
|
126
126
|
],
|
|
127
127
|
};
|
|
128
128
|
}
|
|
@@ -130,52 +130,52 @@ class GenerateAppComponentTool extends MCPTool {
|
|
|
130
130
|
const recommendations = [];
|
|
131
131
|
if (plannedFeatures.length === 0) {
|
|
132
132
|
return [
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
'Consider what sub-components you plan to add to determine required OAuth scopes',
|
|
134
|
+
'Cards typically need CRM object read access',
|
|
135
|
+
'Functions may need broader API access depending on functionality',
|
|
136
|
+
'Webhooks may need specific event subscription scopes',
|
|
137
137
|
];
|
|
138
138
|
}
|
|
139
139
|
const scopeMap = {
|
|
140
140
|
cards: [
|
|
141
|
-
|
|
141
|
+
'Cards typically need: crm.objects.contacts.read, crm.objects.companies.read, crm.objects.deals.read',
|
|
142
142
|
],
|
|
143
143
|
functions: [
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
'Functions may need: broader API access depending on functionality',
|
|
145
|
+
'Consider: crm.objects.*.read, crm.objects.*.write if manipulating CRM data',
|
|
146
146
|
],
|
|
147
147
|
webhooks: [
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
'Webhooks may need: specific event subscription scopes',
|
|
149
|
+
'Consider: webhooks scope and relevant object scopes for events you want to receive',
|
|
150
150
|
],
|
|
151
151
|
settings: [
|
|
152
|
-
|
|
152
|
+
'Settings typically need: minimal scopes, may inherit from other components',
|
|
153
153
|
],
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
'marketing-events': [
|
|
155
|
+
'Marketing events may need: marketing event creation and management scopes',
|
|
156
156
|
],
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
'timeline-events': [
|
|
158
|
+
'Timeline events may need: timeline read/write scopes for the relevant objects',
|
|
159
159
|
],
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
'workflow-actions': [
|
|
161
|
+
'Workflow actions may need: workflow and automation related scopes',
|
|
162
162
|
],
|
|
163
163
|
};
|
|
164
|
-
plannedFeatures.forEach(
|
|
164
|
+
plannedFeatures.forEach(feature => {
|
|
165
165
|
if (scopeMap[feature]) {
|
|
166
|
-
recommendations.push(`For ${feature}: ${scopeMap[feature].join(
|
|
166
|
+
recommendations.push(`For ${feature}: ${scopeMap[feature].join(', ')}`);
|
|
167
167
|
}
|
|
168
168
|
});
|
|
169
169
|
return recommendations;
|
|
170
170
|
}
|
|
171
171
|
generateNextSteps(plannedFeatures, appPath) {
|
|
172
172
|
const steps = [
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
'1. Create the app component configuration file and directory structure',
|
|
174
|
+
'2. Test OAuth flow and ensure authentication works correctly',
|
|
175
|
+
'3. Add sub-components within the app directory as needed:',
|
|
176
176
|
];
|
|
177
177
|
if (plannedFeatures.length > 0) {
|
|
178
|
-
plannedFeatures.forEach(
|
|
178
|
+
plannedFeatures.forEach(feature => {
|
|
179
179
|
steps.push(` - ${appPath}/${feature}/`);
|
|
180
180
|
});
|
|
181
181
|
}
|
|
@@ -186,7 +186,7 @@ class GenerateAppComponentTool extends MCPTool {
|
|
|
186
186
|
steps.push(` - ${appPath}/webhooks/ (for event handling)`);
|
|
187
187
|
}
|
|
188
188
|
steps.push("4. Ensure all sub-components can access required APIs with the app's OAuth scopes");
|
|
189
|
-
steps.push(
|
|
189
|
+
steps.push('5. Test the complete application with all components integrated');
|
|
190
190
|
return steps;
|
|
191
191
|
}
|
|
192
192
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
interface CLICheckResult {
|
|
2
2
|
isInstalled: boolean;
|
|
3
3
|
version?: string;
|
|
4
|
+
debugInfo?: {
|
|
5
|
+
command: string;
|
|
6
|
+
success: boolean;
|
|
7
|
+
output: string;
|
|
8
|
+
error?: string;
|
|
9
|
+
env?: Record<string, string | undefined>;
|
|
10
|
+
};
|
|
4
11
|
}
|
|
5
12
|
interface CommandResult {
|
|
6
13
|
success: boolean;
|
|
@@ -8,6 +15,7 @@ interface CommandResult {
|
|
|
8
15
|
error?: string;
|
|
9
16
|
}
|
|
10
17
|
export declare class HubSpotCLIHelper {
|
|
18
|
+
private execAsync;
|
|
11
19
|
checkCLIInstallation(): Promise<CLICheckResult>;
|
|
12
20
|
installCLI(): Promise<CommandResult>;
|
|
13
21
|
runCommand(command: string): Promise<CommandResult>;
|
|
@@ -1,8 +1,22 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
1
3
|
export class HubSpotCLIHelper {
|
|
4
|
+
execAsync = promisify(exec);
|
|
2
5
|
async checkCLIInstallation() {
|
|
3
6
|
try {
|
|
4
7
|
// Run hs --version to check if CLI is installed
|
|
5
|
-
const result = await this.runCommand(
|
|
8
|
+
const result = await this.runCommand('hs --version');
|
|
9
|
+
const debugInfo = {
|
|
10
|
+
command: 'hs --version',
|
|
11
|
+
success: result.success,
|
|
12
|
+
output: result.output,
|
|
13
|
+
error: result.error,
|
|
14
|
+
env: {
|
|
15
|
+
PATH: process.env.PATH,
|
|
16
|
+
NODE_PATH: process.env.NODE_PATH,
|
|
17
|
+
npm_config_prefix: process.env.npm_config_prefix,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
6
20
|
if (result.success) {
|
|
7
21
|
// Parse version from output (typically like "@hubspot/cli/4.0.0 darwin-x64 node-v18.17.0")
|
|
8
22
|
const versionMatch = result.output.match(/@hubspot\/cli\/(\d+\.\d+\.\d+)/);
|
|
@@ -10,62 +24,84 @@ export class HubSpotCLIHelper {
|
|
|
10
24
|
return {
|
|
11
25
|
isInstalled: true,
|
|
12
26
|
version: version,
|
|
27
|
+
debugInfo,
|
|
13
28
|
};
|
|
14
29
|
}
|
|
15
30
|
else {
|
|
16
|
-
return {
|
|
31
|
+
return {
|
|
32
|
+
isInstalled: false,
|
|
33
|
+
debugInfo,
|
|
34
|
+
};
|
|
17
35
|
}
|
|
18
36
|
}
|
|
19
37
|
catch (error) {
|
|
20
|
-
return {
|
|
38
|
+
return {
|
|
39
|
+
isInstalled: false,
|
|
40
|
+
debugInfo: {
|
|
41
|
+
command: 'hs --version',
|
|
42
|
+
success: false,
|
|
43
|
+
output: '',
|
|
44
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
45
|
+
env: {
|
|
46
|
+
PATH: process.env.PATH,
|
|
47
|
+
NODE_PATH: process.env.NODE_PATH,
|
|
48
|
+
npm_config_prefix: process.env.npm_config_prefix,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
21
52
|
}
|
|
22
53
|
}
|
|
23
54
|
async installCLI() {
|
|
24
55
|
try {
|
|
25
|
-
return await this.runCommand(
|
|
56
|
+
return await this.runCommand('npm install -g @hubspot/cli');
|
|
26
57
|
}
|
|
27
58
|
catch (error) {
|
|
28
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
59
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
29
60
|
return {
|
|
30
61
|
success: false,
|
|
31
|
-
output:
|
|
62
|
+
output: '',
|
|
32
63
|
error: errorMessage,
|
|
33
64
|
};
|
|
34
65
|
}
|
|
35
66
|
}
|
|
36
67
|
async runCommand(command) {
|
|
37
68
|
try {
|
|
38
|
-
//
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
69
|
+
// Use the same environment as the current process, including PATH
|
|
70
|
+
const result = await this.execAsync(command, {
|
|
71
|
+
env: process.env,
|
|
72
|
+
shell: '/bin/bash',
|
|
73
|
+
});
|
|
43
74
|
return {
|
|
44
75
|
success: true,
|
|
45
|
-
output: result.stdout || result.stderr ||
|
|
76
|
+
output: result.stdout || result.stderr || '',
|
|
46
77
|
};
|
|
47
78
|
}
|
|
48
79
|
catch (error) {
|
|
49
80
|
// Command failed (e.g., command not found, non-zero exit code)
|
|
81
|
+
const err = error;
|
|
50
82
|
return {
|
|
51
83
|
success: false,
|
|
52
|
-
output:
|
|
53
|
-
error:
|
|
84
|
+
output: err.stdout || '',
|
|
85
|
+
error: err.stderr || err.message || 'Command execution failed',
|
|
54
86
|
};
|
|
55
87
|
}
|
|
56
88
|
}
|
|
57
|
-
async runProjectCommand(command, projectPath =
|
|
89
|
+
async runProjectCommand(command, projectPath = '.') {
|
|
58
90
|
try {
|
|
91
|
+
// Get absolute path to avoid any relative path issues
|
|
92
|
+
const path = await import('path');
|
|
93
|
+
const absoluteProjectPath = path.resolve(projectPath);
|
|
94
|
+
// Add debugging information about directories
|
|
95
|
+
const debugCommand = `echo "MCP Server CWD: $(pwd)" && echo "Project Path: ${absoluteProjectPath}" && ls -la "${absoluteProjectPath}" | head -5`;
|
|
59
96
|
// Change to the project directory and run command
|
|
60
|
-
const
|
|
61
|
-
const fullCommand = `${cdCommand}${command}`;
|
|
97
|
+
const fullCommand = `cd "${absoluteProjectPath}" && ${debugCommand} && ${command}`;
|
|
62
98
|
return await this.runCommand(fullCommand);
|
|
63
99
|
}
|
|
64
100
|
catch (error) {
|
|
65
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
101
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
66
102
|
return {
|
|
67
103
|
success: false,
|
|
68
|
-
output:
|
|
104
|
+
output: '',
|
|
69
105
|
error: errorMessage,
|
|
70
106
|
};
|
|
71
107
|
}
|
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
import { MCPTool } from
|
|
2
|
-
import { z } from
|
|
3
|
-
import HubSpotCLIHelper from
|
|
1
|
+
import { MCPTool } from 'mcp-framework';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import HubSpotCLIHelper from './HubSpotCLIHelper.js';
|
|
4
4
|
class UploadProjectTool extends MCPTool {
|
|
5
|
-
name =
|
|
6
|
-
description =
|
|
5
|
+
name = 'uploadProject';
|
|
6
|
+
description = 'Uploads a developer project to HubSpot using the HubSpot CLI';
|
|
7
7
|
cliHelper = new HubSpotCLIHelper();
|
|
8
8
|
schema = {
|
|
9
9
|
projectPath: {
|
|
10
10
|
type: z.string().optional(),
|
|
11
|
-
description:
|
|
11
|
+
description: 'Path to the project directory. When using MCP server globally, provide the absolute path to your project (e.g., "/Users/yourname/projects/my-hubspot-project"). Defaults to "." for local usage.',
|
|
12
12
|
},
|
|
13
13
|
accountId: {
|
|
14
14
|
type: z.string().optional(),
|
|
15
|
-
description:
|
|
15
|
+
description: 'HubSpot account ID to upload to. If not provided, uses CLI default or prompts',
|
|
16
16
|
},
|
|
17
17
|
autoInstallCLI: {
|
|
18
18
|
type: z.boolean().optional(),
|
|
19
|
-
description:
|
|
19
|
+
description: 'Whether to automatically install HubSpot CLI if not found. Defaults to false',
|
|
20
20
|
},
|
|
21
21
|
};
|
|
22
22
|
async execute(input) {
|
|
23
|
-
|
|
23
|
+
// Default to current directory "." which should resolve relative to where the MCP client is running
|
|
24
|
+
const { projectPath = '.', accountId, autoInstallCLI = false } = input;
|
|
24
25
|
try {
|
|
25
26
|
// Step 1: Check if HubSpot CLI is installed
|
|
26
27
|
const cliCheckResult = await this.cliHelper.checkCLIInstallation();
|
|
@@ -30,20 +31,20 @@ class UploadProjectTool extends MCPTool {
|
|
|
30
31
|
const installResult = await this.cliHelper.installCLI();
|
|
31
32
|
if (!installResult.success) {
|
|
32
33
|
return {
|
|
33
|
-
status:
|
|
34
|
-
message:
|
|
34
|
+
status: 'cli-install-failed',
|
|
35
|
+
message: '❌ Failed to install HubSpot CLI automatically',
|
|
35
36
|
error: installResult.error,
|
|
36
37
|
actions: [
|
|
37
38
|
{
|
|
38
|
-
action:
|
|
39
|
-
command:
|
|
40
|
-
description:
|
|
39
|
+
action: 'install-cli-manually',
|
|
40
|
+
command: 'npm install -g @hubspot/cli',
|
|
41
|
+
description: 'Install HubSpot CLI manually',
|
|
41
42
|
},
|
|
42
43
|
],
|
|
43
44
|
troubleshooting: [
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
'Try installing the CLI manually: npm install -g @hubspot/cli',
|
|
46
|
+
'Ensure you have npm installed and proper permissions',
|
|
47
|
+
'Run the upload command again after CLI installation',
|
|
47
48
|
],
|
|
48
49
|
};
|
|
49
50
|
}
|
|
@@ -51,37 +52,38 @@ class UploadProjectTool extends MCPTool {
|
|
|
51
52
|
const recheckResult = await this.cliHelper.checkCLIInstallation();
|
|
52
53
|
if (!recheckResult.isInstalled) {
|
|
53
54
|
return {
|
|
54
|
-
status:
|
|
55
|
-
message:
|
|
55
|
+
status: 'cli-install-verification-failed',
|
|
56
|
+
message: '❌ CLI installation completed but verification failed',
|
|
56
57
|
troubleshooting: [
|
|
57
58
|
"Try running 'hs --version' manually to verify installation",
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
'Restart your terminal/shell session',
|
|
60
|
+
'Check if the CLI was installed to the correct PATH',
|
|
60
61
|
],
|
|
61
62
|
};
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
else {
|
|
65
66
|
return {
|
|
66
|
-
status:
|
|
67
|
-
message:
|
|
67
|
+
status: 'cli-not-found',
|
|
68
|
+
message: 'HubSpot CLI not found. Install it to upload projects.',
|
|
69
|
+
error: `Debug info: ${JSON.stringify(cliCheckResult.debugInfo, null, 2)}`,
|
|
68
70
|
actions: [
|
|
69
71
|
{
|
|
70
|
-
action:
|
|
71
|
-
command:
|
|
72
|
-
description:
|
|
72
|
+
action: 'install-cli',
|
|
73
|
+
command: 'npm install -g @hubspot/cli',
|
|
74
|
+
description: 'Install HubSpot CLI globally',
|
|
73
75
|
},
|
|
74
76
|
],
|
|
75
77
|
nextSteps: [
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
'1. Install HubSpot CLI globally',
|
|
79
|
+
'2. Authenticate with your HubSpot account (hs auth)',
|
|
80
|
+
'3. Run project upload again',
|
|
79
81
|
],
|
|
80
82
|
};
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
// Step 2: Build the upload command
|
|
84
|
-
let uploadCommand =
|
|
86
|
+
let uploadCommand = 'hs project upload';
|
|
85
87
|
if (accountId) {
|
|
86
88
|
uploadCommand += ` --account=${accountId}`;
|
|
87
89
|
}
|
|
@@ -91,56 +93,71 @@ class UploadProjectTool extends MCPTool {
|
|
|
91
93
|
// Parse successful upload output
|
|
92
94
|
const output = uploadResult.output;
|
|
93
95
|
return {
|
|
94
|
-
status:
|
|
95
|
-
message:
|
|
96
|
+
status: 'success',
|
|
97
|
+
message: '✅ Project uploaded successfully to HubSpot!',
|
|
96
98
|
cliVersion: cliCheckResult.version,
|
|
97
99
|
uploadOutput: output,
|
|
98
100
|
accountId: accountId,
|
|
99
101
|
projectPath: projectPath,
|
|
100
102
|
nextSteps: [
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
'1. Check your HubSpot account to verify the project appears',
|
|
104
|
+
'2. Test your components in the HubSpot environment',
|
|
105
|
+
'3. Monitor for any runtime errors or issues',
|
|
104
106
|
"4. Use 'hs project logs' to view application logs if needed",
|
|
105
107
|
],
|
|
106
108
|
};
|
|
107
109
|
}
|
|
108
110
|
else {
|
|
109
111
|
// Parse upload failure
|
|
110
|
-
const errorOutput = uploadResult.output || uploadResult.error ||
|
|
112
|
+
const errorOutput = uploadResult.output || uploadResult.error || 'Upload failed';
|
|
113
|
+
// Check if this looks like a path issue with global MCP server
|
|
114
|
+
const isPathIssue = errorOutput.includes('Unable to locate a project configuration file') || errorOutput.includes('MCP Server CWD: /');
|
|
111
115
|
return {
|
|
112
|
-
status:
|
|
113
|
-
message:
|
|
116
|
+
status: 'upload-failed',
|
|
117
|
+
message: '❌ Project upload failed',
|
|
114
118
|
cliVersion: cliCheckResult.version,
|
|
115
119
|
uploadOutput: errorOutput,
|
|
116
120
|
error: uploadResult.error,
|
|
117
121
|
troubleshooting: [
|
|
118
122
|
"Ensure you're authenticated: run 'hs auth' to login",
|
|
119
|
-
|
|
123
|
+
'Verify your account has permission to upload projects',
|
|
120
124
|
"Check that your project is valid: run 'hs project validate' first",
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
'Ensure you have network connectivity to HubSpot',
|
|
126
|
+
'Try uploading to a different account if you have access to multiple',
|
|
127
|
+
`Working directory was: ${projectPath}`,
|
|
128
|
+
...(isPathIssue
|
|
129
|
+
? [
|
|
130
|
+
'⚠️ PATH ISSUE DETECTED: You may be using the global MCP server',
|
|
131
|
+
'When using global MCP server, provide the absolute path to your project:',
|
|
132
|
+
'Example: {"projectPath": "/Users/yourname/projects/my-hubspot-project"}',
|
|
133
|
+
'Or consider running the MCP server locally from your project directory',
|
|
134
|
+
]
|
|
135
|
+
: []),
|
|
123
136
|
],
|
|
124
137
|
nextSteps: [
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
138
|
+
'1. Check authentication status: hs auth info',
|
|
139
|
+
'2. Validate project first: hs project validate',
|
|
140
|
+
...(isPathIssue
|
|
141
|
+
? ['3. Provide absolute projectPath if using global MCP server']
|
|
142
|
+
: []),
|
|
143
|
+
'4. Review the error output above for specific issues',
|
|
144
|
+
'5. Try the upload command again after fixing issues',
|
|
129
145
|
],
|
|
130
146
|
};
|
|
131
147
|
}
|
|
132
148
|
}
|
|
133
149
|
catch (error) {
|
|
134
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
150
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
135
151
|
return {
|
|
136
|
-
status:
|
|
137
|
-
message:
|
|
152
|
+
status: 'error',
|
|
153
|
+
message: 'An error occurred during project upload',
|
|
138
154
|
error: errorMessage,
|
|
139
155
|
troubleshooting: [
|
|
140
156
|
"Ensure you're in a valid developer project directory",
|
|
141
|
-
|
|
142
|
-
|
|
157
|
+
'Check that hsproject.json exists in the project root',
|
|
158
|
+
'Verify HubSpot CLI is properly installed and accessible',
|
|
143
159
|
"Try running 'hs project upload' manually to see detailed output",
|
|
160
|
+
`Attempted project path: ${projectPath}`,
|
|
144
161
|
],
|
|
145
162
|
};
|
|
146
163
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MCPTool } from
|
|
2
|
-
import { z } from
|
|
1
|
+
import { MCPTool } from 'mcp-framework';
|
|
2
|
+
import { z } from 'zod';
|
|
3
3
|
interface ValidateProjectInput {
|
|
4
4
|
autoFix?: boolean;
|
|
5
5
|
projectPath?: string;
|
|
@@ -13,7 +13,7 @@ interface ValidationResponse {
|
|
|
13
13
|
errorAnalysis?: Array<{
|
|
14
14
|
category: string;
|
|
15
15
|
issue: string;
|
|
16
|
-
severity:
|
|
16
|
+
severity: 'error' | 'warning';
|
|
17
17
|
component?: string;
|
|
18
18
|
file?: string;
|
|
19
19
|
}>;
|
|
@@ -1,40 +1,42 @@
|
|
|
1
|
-
import { MCPTool } from
|
|
2
|
-
import { z } from
|
|
3
|
-
import HubSpotCLIHelper from
|
|
1
|
+
import { MCPTool } from 'mcp-framework';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import HubSpotCLIHelper from './HubSpotCLIHelper.js';
|
|
4
4
|
class ValidateProjectTool extends MCPTool {
|
|
5
|
-
name =
|
|
6
|
-
description =
|
|
5
|
+
name = 'validateProject';
|
|
6
|
+
description = 'Validates a developer project using the HubSpot CLI and provides guidance for fixing any validation errors';
|
|
7
7
|
cliHelper = new HubSpotCLIHelper();
|
|
8
8
|
schema = {
|
|
9
9
|
autoFix: {
|
|
10
10
|
type: z.boolean().optional(),
|
|
11
|
-
description:
|
|
11
|
+
description: 'Whether to attempt automatic fixes for common validation errors. Defaults to false',
|
|
12
12
|
},
|
|
13
13
|
projectPath: {
|
|
14
14
|
type: z.string().optional(),
|
|
15
|
-
description:
|
|
15
|
+
description: 'Path to the project directory. When using MCP server globally, provide the absolute path to your project (e.g., "/Users/yourname/projects/my-hubspot-project"). Defaults to "." for local usage.',
|
|
16
16
|
},
|
|
17
17
|
};
|
|
18
18
|
async execute(input) {
|
|
19
|
-
|
|
19
|
+
// Default to current directory "." which should resolve relative to where the MCP client is running
|
|
20
|
+
const { autoFix = false, projectPath = '.' } = input;
|
|
20
21
|
try {
|
|
21
22
|
// Step 1: Check if HubSpot CLI is installed
|
|
22
23
|
const cliCheckResult = await this.cliHelper.checkCLIInstallation();
|
|
23
24
|
if (!cliCheckResult.isInstalled) {
|
|
24
25
|
return {
|
|
25
|
-
status:
|
|
26
|
-
|
|
26
|
+
status: 'cli-not-found',
|
|
27
|
+
error: `Debug info: ${JSON.stringify(cliCheckResult.debugInfo, null, 2)}`,
|
|
28
|
+
message: 'HubSpot CLI not found. Installing...',
|
|
27
29
|
actions: [
|
|
28
30
|
{
|
|
29
|
-
action:
|
|
30
|
-
command:
|
|
31
|
-
description:
|
|
31
|
+
action: 'install-cli',
|
|
32
|
+
command: 'npm install -g @hubspot/cli',
|
|
33
|
+
description: 'Installing HubSpot CLI globally',
|
|
32
34
|
},
|
|
33
35
|
],
|
|
34
36
|
nextSteps: [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
'1. Install HubSpot CLI globally',
|
|
38
|
+
'2. Run project validation again',
|
|
39
|
+
'3. Follow any additional guidance for fixing validation errors',
|
|
38
40
|
],
|
|
39
41
|
};
|
|
40
42
|
}
|
|
@@ -42,18 +44,18 @@ class ValidateProjectTool extends MCPTool {
|
|
|
42
44
|
const validationResult = await this.runProjectValidation(projectPath);
|
|
43
45
|
if (validationResult.isValid) {
|
|
44
46
|
return {
|
|
45
|
-
status:
|
|
46
|
-
message:
|
|
47
|
+
status: 'valid',
|
|
48
|
+
message: '✅ Project validation passed! Your developer project is properly configured.',
|
|
47
49
|
cliVersion: cliCheckResult.version,
|
|
48
50
|
validationOutput: validationResult.output,
|
|
49
|
-
projectStructure:
|
|
51
|
+
projectStructure: 'All components and configurations follow platform conventions',
|
|
50
52
|
};
|
|
51
53
|
}
|
|
52
54
|
// Step 3: Parse validation errors and provide guidance
|
|
53
55
|
const errorAnalysis = this.analyzeValidationErrors(validationResult.errors);
|
|
54
56
|
const suggestions = this.generateFixSuggestions(errorAnalysis);
|
|
55
57
|
const response = {
|
|
56
|
-
status:
|
|
58
|
+
status: 'validation-failed',
|
|
57
59
|
message: "❌ Project validation failed. Here's how to fix the issues:",
|
|
58
60
|
cliVersion: cliCheckResult.version,
|
|
59
61
|
validationOutput: validationResult.output,
|
|
@@ -68,36 +70,49 @@ class ValidateProjectTool extends MCPTool {
|
|
|
68
70
|
return response;
|
|
69
71
|
}
|
|
70
72
|
catch (error) {
|
|
71
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
73
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
72
74
|
return {
|
|
73
|
-
status:
|
|
74
|
-
message:
|
|
75
|
+
status: 'error',
|
|
76
|
+
message: 'An error occurred during project validation',
|
|
75
77
|
error: errorMessage,
|
|
76
78
|
troubleshooting: [
|
|
77
79
|
"Ensure you're in a valid developer project directory",
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
'Check that hsproject.json exists in the project root',
|
|
81
|
+
'Verify HubSpot CLI is properly installed and accessible',
|
|
80
82
|
"Try running 'hs project validate' manually to see detailed output",
|
|
83
|
+
`Attempted project path: ${projectPath}`,
|
|
81
84
|
],
|
|
82
85
|
};
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
async runProjectValidation(projectPath) {
|
|
86
89
|
try {
|
|
87
|
-
const result = await this.cliHelper.runProjectCommand(
|
|
90
|
+
const result = await this.cliHelper.runProjectCommand('hs project validate', projectPath);
|
|
88
91
|
if (result.success) {
|
|
89
92
|
// Parse the validation output to determine if it passed
|
|
90
93
|
const output = result.output;
|
|
91
94
|
// Check for validation success indicators
|
|
92
|
-
const isValid = output.includes(
|
|
93
|
-
output.includes(
|
|
94
|
-
output.includes(
|
|
95
|
-
!output.includes(
|
|
95
|
+
const isValid = output.includes('✅') ||
|
|
96
|
+
output.includes('Project validation passed') ||
|
|
97
|
+
output.includes('valid') ||
|
|
98
|
+
!output.includes('Project validation failed:');
|
|
96
99
|
// Parse structured validation errors if present
|
|
97
100
|
const errors = [];
|
|
98
|
-
if (!isValid && output.includes(
|
|
101
|
+
if (!isValid && output.includes('Project validation failed:')) {
|
|
99
102
|
errors.push(...this.parseValidationErrors(output));
|
|
100
103
|
}
|
|
104
|
+
// Add debugging information about directory context
|
|
105
|
+
if (result.output.includes('Unable to locate a project configuration file')) {
|
|
106
|
+
errors.push(`Working directory was: ${projectPath}`);
|
|
107
|
+
errors.push("Make sure you're running this from a directory containing hsproject.json");
|
|
108
|
+
// Check if this looks like a global MCP server path issue
|
|
109
|
+
if (result.output.includes('MCP Server CWD: /')) {
|
|
110
|
+
errors.push('⚠️ PATH ISSUE DETECTED: You may be using the global MCP server');
|
|
111
|
+
errors.push('When using global MCP server, provide the absolute path to your project:');
|
|
112
|
+
errors.push('Example: {"projectPath": "/Users/yourname/projects/my-hubspot-project"}');
|
|
113
|
+
errors.push('Or consider running the MCP server locally from your project directory');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
101
116
|
return {
|
|
102
117
|
isValid: isValid,
|
|
103
118
|
output: output,
|
|
@@ -106,16 +121,22 @@ class ValidateProjectTool extends MCPTool {
|
|
|
106
121
|
}
|
|
107
122
|
else {
|
|
108
123
|
// Command failed to run
|
|
109
|
-
const errorMessage = result.error ||
|
|
124
|
+
const errorMessage = result.error || 'Failed to run project validation';
|
|
125
|
+
const errors = [errorMessage];
|
|
126
|
+
// Add debugging information about directory context
|
|
127
|
+
if (result.output.includes('Unable to locate a project configuration file')) {
|
|
128
|
+
errors.push(`Working directory was: ${projectPath}`);
|
|
129
|
+
errors.push("Make sure you're running this from a directory containing hsproject.json");
|
|
130
|
+
}
|
|
110
131
|
return {
|
|
111
132
|
isValid: false,
|
|
112
|
-
output: result.output ||
|
|
113
|
-
errors:
|
|
133
|
+
output: result.output || '',
|
|
134
|
+
errors: errors,
|
|
114
135
|
};
|
|
115
136
|
}
|
|
116
137
|
}
|
|
117
138
|
catch (error) {
|
|
118
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
139
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown validation error';
|
|
119
140
|
throw new Error(`Failed to run project validation: ${errorMessage}`);
|
|
120
141
|
}
|
|
121
142
|
}
|
|
@@ -125,8 +146,8 @@ class ValidateProjectTool extends MCPTool {
|
|
|
125
146
|
}
|
|
126
147
|
parseValidationErrors(output) {
|
|
127
148
|
const errors = [];
|
|
128
|
-
const lines = output.split(
|
|
129
|
-
let currentFile =
|
|
149
|
+
const lines = output.split('\n');
|
|
150
|
+
let currentFile = '';
|
|
130
151
|
let inErrorSection = false;
|
|
131
152
|
for (const line of lines) {
|
|
132
153
|
// Look for file error headers like "Encountered the following errors for src/app/file.json:"
|
|
@@ -138,7 +159,7 @@ class ValidateProjectTool extends MCPTool {
|
|
|
138
159
|
}
|
|
139
160
|
// Parse individual error lines (start with tab and dash)
|
|
140
161
|
if (inErrorSection && line.match(/^\s*-\s+/)) {
|
|
141
|
-
const errorText = line.replace(/^\s*-\s+/,
|
|
162
|
+
const errorText = line.replace(/^\s*-\s+/, '').trim();
|
|
142
163
|
if (errorText) {
|
|
143
164
|
errors.push(`${currentFile}: ${errorText}`);
|
|
144
165
|
}
|
|
@@ -146,7 +167,7 @@ class ValidateProjectTool extends MCPTool {
|
|
|
146
167
|
}
|
|
147
168
|
// Handle multi-line errors with additional indentation
|
|
148
169
|
if (inErrorSection && line.match(/^\s{2,}-\s+/)) {
|
|
149
|
-
const errorText = line.replace(/^\s*-\s+/,
|
|
170
|
+
const errorText = line.replace(/^\s*-\s+/, '').trim();
|
|
150
171
|
if (errorText && errors.length > 0) {
|
|
151
172
|
// Append to the last error as additional context
|
|
152
173
|
errors[errors.length - 1] += `\n ${errorText}`;
|
|
@@ -154,10 +175,10 @@ class ValidateProjectTool extends MCPTool {
|
|
|
154
175
|
continue;
|
|
155
176
|
}
|
|
156
177
|
// Reset when we hit an empty line or new section
|
|
157
|
-
if (line.trim() ===
|
|
158
|
-
(!line.startsWith(
|
|
178
|
+
if (line.trim() === '' ||
|
|
179
|
+
(!line.startsWith('\t') && !line.startsWith(' '))) {
|
|
159
180
|
inErrorSection = false;
|
|
160
|
-
currentFile =
|
|
181
|
+
currentFile = '';
|
|
161
182
|
}
|
|
162
183
|
}
|
|
163
184
|
return errors;
|
|
@@ -165,37 +186,37 @@ class ValidateProjectTool extends MCPTool {
|
|
|
165
186
|
analyzeValidationErrors(errors) {
|
|
166
187
|
const analysis = [];
|
|
167
188
|
for (const error of errors) {
|
|
168
|
-
const filePath = error.split(
|
|
169
|
-
const errorMessage = error.substring(error.indexOf(
|
|
170
|
-
let category =
|
|
171
|
-
let component =
|
|
172
|
-
let severity =
|
|
189
|
+
const filePath = error.split(':')[0];
|
|
190
|
+
const errorMessage = error.substring(error.indexOf(':') + 1).trim();
|
|
191
|
+
let category = 'unknown';
|
|
192
|
+
let component = 'unknown';
|
|
193
|
+
let severity = 'error';
|
|
173
194
|
// Extract file information
|
|
174
|
-
if (filePath.includes(
|
|
175
|
-
category =
|
|
176
|
-
component =
|
|
195
|
+
if (filePath.includes('hsproject.json')) {
|
|
196
|
+
category = 'project-config';
|
|
197
|
+
component = 'project';
|
|
177
198
|
}
|
|
178
|
-
else if (filePath.includes(
|
|
179
|
-
category =
|
|
199
|
+
else if (filePath.includes('-hsmeta.json')) {
|
|
200
|
+
category = 'component-config';
|
|
180
201
|
component = this.extractComponentFromPath(filePath);
|
|
181
202
|
}
|
|
182
|
-
else if (filePath.includes(
|
|
183
|
-
category =
|
|
203
|
+
else if (filePath.includes('package.json')) {
|
|
204
|
+
category = 'dependencies';
|
|
184
205
|
component = this.extractComponentFromPath(filePath);
|
|
185
|
-
severity =
|
|
206
|
+
severity = 'warning';
|
|
186
207
|
}
|
|
187
208
|
// Analyze error message content
|
|
188
|
-
if (errorMessage.includes(
|
|
189
|
-
category =
|
|
209
|
+
if (errorMessage.includes('Invalid JSON')) {
|
|
210
|
+
category = 'json-syntax';
|
|
190
211
|
}
|
|
191
|
-
else if (errorMessage.includes(
|
|
192
|
-
category =
|
|
212
|
+
else if (errorMessage.includes('Missing required field')) {
|
|
213
|
+
category = 'required-field';
|
|
193
214
|
}
|
|
194
|
-
else if (errorMessage.includes(
|
|
195
|
-
category =
|
|
215
|
+
else if (errorMessage.includes('must NOT have additional properties')) {
|
|
216
|
+
category = 'schema-validation';
|
|
196
217
|
}
|
|
197
|
-
else if (errorMessage.includes(
|
|
198
|
-
category =
|
|
218
|
+
else if (errorMessage.includes('entrypoint')) {
|
|
219
|
+
category = 'entrypoint';
|
|
199
220
|
}
|
|
200
221
|
analysis.push({
|
|
201
222
|
category: category,
|
|
@@ -209,46 +230,46 @@ class ValidateProjectTool extends MCPTool {
|
|
|
209
230
|
}
|
|
210
231
|
extractComponentFromPath(filePath) {
|
|
211
232
|
// Extract component type from file path like "src/app/cards/my-card-hsmeta.json"
|
|
212
|
-
if (filePath.includes(
|
|
213
|
-
const pathParts = filePath.split(
|
|
233
|
+
if (filePath.includes('/app/')) {
|
|
234
|
+
const pathParts = filePath.split('/app/')[1].split('/');
|
|
214
235
|
if (pathParts.length > 1) {
|
|
215
236
|
return pathParts[0]; // e.g., "cards", "functions", etc.
|
|
216
237
|
}
|
|
217
|
-
return
|
|
238
|
+
return 'app';
|
|
218
239
|
}
|
|
219
240
|
// Handle top-level app component files
|
|
220
|
-
if (filePath.includes(
|
|
221
|
-
return
|
|
241
|
+
if (filePath.includes('-hsmeta.json')) {
|
|
242
|
+
return 'app';
|
|
222
243
|
}
|
|
223
|
-
return
|
|
244
|
+
return 'unknown';
|
|
224
245
|
}
|
|
225
246
|
generateFixSuggestions(errorAnalysis) {
|
|
226
247
|
const suggestions = [];
|
|
227
248
|
for (const error of errorAnalysis) {
|
|
228
249
|
switch (error.category) {
|
|
229
|
-
case
|
|
230
|
-
suggestions.push("Fix hsproject.json: Ensure it contains 'name', 'srcDir', and 'platformVersion' fields",
|
|
250
|
+
case 'project-config':
|
|
251
|
+
suggestions.push("Fix hsproject.json: Ensure it contains 'name', 'srcDir', and 'platformVersion' fields", 'Use the generateProjectConfig tool to create a valid hsproject.json file');
|
|
231
252
|
break;
|
|
232
|
-
case
|
|
233
|
-
suggestions.push(`Fix ${error.component} component: Check the *-hsmeta.json file structure`,
|
|
253
|
+
case 'component-config':
|
|
254
|
+
suggestions.push(`Fix ${error.component} component: Check the *-hsmeta.json file structure`, 'Ensure the component follows the envelope pattern: uid, type, config', `Use the appropriate generate component tool for ${error.component} type`);
|
|
234
255
|
break;
|
|
235
|
-
case
|
|
236
|
-
suggestions.push(`Fix JSON syntax error in ${error.file}`,
|
|
256
|
+
case 'json-syntax':
|
|
257
|
+
suggestions.push(`Fix JSON syntax error in ${error.file}`, 'Check for missing commas, brackets, or quotes in the JSON file', 'Use a JSON validator to identify the specific syntax issue');
|
|
237
258
|
break;
|
|
238
|
-
case
|
|
239
|
-
suggestions.push(`Add missing required field: ${this.extractFieldFromError(error.issue)}`, `Update ${error.file} to include all required fields`,
|
|
259
|
+
case 'required-field':
|
|
260
|
+
suggestions.push(`Add missing required field: ${this.extractFieldFromError(error.issue)}`, `Update ${error.file} to include all required fields`, 'Refer to the platform documentation for required field specifications');
|
|
240
261
|
break;
|
|
241
|
-
case
|
|
242
|
-
suggestions.push(`Fix schema validation error in ${error.file}`,
|
|
262
|
+
case 'schema-validation':
|
|
263
|
+
suggestions.push(`Fix schema validation error in ${error.file}`, 'Remove additional properties that are not allowed', `Check ${error.component} component schema requirements`, 'Ensure all field types match the expected schema');
|
|
243
264
|
break;
|
|
244
|
-
case
|
|
245
|
-
suggestions.push(
|
|
265
|
+
case 'dependencies':
|
|
266
|
+
suggestions.push('Fix package.json: Ensure React and @hubspot/ui-extensions dependencies are present', "Run 'npm install' to install missing dependencies", 'Check that package.json is in the correct component directory');
|
|
246
267
|
break;
|
|
247
|
-
case
|
|
248
|
-
suggestions.push(
|
|
268
|
+
case 'entrypoint':
|
|
269
|
+
suggestions.push('Fix component entrypoint: Ensure the React file exists at the specified path', 'Check that the entrypoint path in *-hsmeta.json matches the actual file location', 'Verify the React component exports properly');
|
|
249
270
|
break;
|
|
250
271
|
default:
|
|
251
|
-
suggestions.push(`Review error in ${error.file ||
|
|
272
|
+
suggestions.push(`Review error in ${error.file || 'project'}: ${error.issue}`);
|
|
252
273
|
}
|
|
253
274
|
}
|
|
254
275
|
return [...new Set(suggestions)]; // Remove duplicates
|
|
@@ -256,53 +277,53 @@ class ValidateProjectTool extends MCPTool {
|
|
|
256
277
|
extractFieldFromError(errorMessage) {
|
|
257
278
|
// Extract field name from messages like "Missing required field: config.description"
|
|
258
279
|
const match = errorMessage.match(/Missing required field:\s*(.+)/);
|
|
259
|
-
return match ? match[1] :
|
|
280
|
+
return match ? match[1] : 'unknown field';
|
|
260
281
|
}
|
|
261
282
|
generateNextSteps(errorAnalysis, autoFix) {
|
|
262
283
|
const steps = [];
|
|
263
284
|
if (autoFix) {
|
|
264
|
-
steps.push(
|
|
285
|
+
steps.push('1. Review auto-fix actions and apply them');
|
|
265
286
|
}
|
|
266
287
|
else {
|
|
267
|
-
steps.push(
|
|
288
|
+
steps.push('1. Review the validation errors and suggestions above');
|
|
268
289
|
}
|
|
269
|
-
const hasProjectConfig = errorAnalysis.some(
|
|
270
|
-
const hasComponentIssues = errorAnalysis.some(
|
|
271
|
-
const hasDependencyIssues = errorAnalysis.some(
|
|
290
|
+
const hasProjectConfig = errorAnalysis.some(e => e.category === 'project-config');
|
|
291
|
+
const hasComponentIssues = errorAnalysis.some(e => e.category === 'component-config');
|
|
292
|
+
const hasDependencyIssues = errorAnalysis.some(e => e.category === 'dependencies');
|
|
272
293
|
if (hasProjectConfig) {
|
|
273
|
-
steps.push(
|
|
294
|
+
steps.push('2. Fix hsproject.json configuration issues first');
|
|
274
295
|
}
|
|
275
296
|
if (hasComponentIssues) {
|
|
276
|
-
steps.push(
|
|
297
|
+
steps.push('3. Fix component configuration issues');
|
|
277
298
|
}
|
|
278
299
|
if (hasDependencyIssues) {
|
|
279
|
-
steps.push(
|
|
300
|
+
steps.push('4. Install missing dependencies');
|
|
280
301
|
}
|
|
281
|
-
steps.push(
|
|
282
|
-
steps.push(
|
|
302
|
+
steps.push('5. Run validation again to verify fixes');
|
|
303
|
+
steps.push('6. Test your components work correctly in development');
|
|
283
304
|
return steps;
|
|
284
305
|
}
|
|
285
306
|
generateAutoFixActions(errorAnalysis) {
|
|
286
307
|
const actions = [];
|
|
287
308
|
for (const error of errorAnalysis) {
|
|
288
309
|
switch (error.category) {
|
|
289
|
-
case
|
|
310
|
+
case 'project-config':
|
|
290
311
|
actions.push({
|
|
291
|
-
action:
|
|
292
|
-
description:
|
|
293
|
-
tool:
|
|
312
|
+
action: 'fix-project-config',
|
|
313
|
+
description: 'Generate valid hsproject.json configuration',
|
|
314
|
+
tool: 'generateProjectConfig',
|
|
294
315
|
});
|
|
295
316
|
break;
|
|
296
|
-
case
|
|
317
|
+
case 'dependencies':
|
|
297
318
|
actions.push({
|
|
298
|
-
action:
|
|
299
|
-
description:
|
|
300
|
-
command:
|
|
319
|
+
action: 'install-dependencies',
|
|
320
|
+
description: 'Install missing npm dependencies',
|
|
321
|
+
command: 'npm install',
|
|
301
322
|
});
|
|
302
323
|
break;
|
|
303
|
-
case
|
|
324
|
+
case 'component-config':
|
|
304
325
|
actions.push({
|
|
305
|
-
action:
|
|
326
|
+
action: 'fix-component-config',
|
|
306
327
|
description: `Fix ${error.component} component configuration`,
|
|
307
328
|
tool: `generate${error.component}Component`,
|
|
308
329
|
});
|
package/package.json
CHANGED