@hubspot/cli 7.7.0-experimental.0 → 7.7.0-experimental.1
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 +54 -50
- package/mcp-server/tools/ValidateProjectTool.d.ts +3 -3
- package/mcp-server/tools/ValidateProjectTool.js +114 -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. Defaults to current directory',
|
|
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 working directory, which should be where Cursor is running from
|
|
24
|
+
const { projectPath = process.cwd(), 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,58 @@ 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';
|
|
111
113
|
return {
|
|
112
|
-
status:
|
|
113
|
-
message:
|
|
114
|
+
status: 'upload-failed',
|
|
115
|
+
message: '❌ Project upload failed',
|
|
114
116
|
cliVersion: cliCheckResult.version,
|
|
115
117
|
uploadOutput: errorOutput,
|
|
116
118
|
error: uploadResult.error,
|
|
117
119
|
troubleshooting: [
|
|
118
120
|
"Ensure you're authenticated: run 'hs auth' to login",
|
|
119
|
-
|
|
121
|
+
'Verify your account has permission to upload projects',
|
|
120
122
|
"Check that your project is valid: run 'hs project validate' first",
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
'Ensure you have network connectivity to HubSpot',
|
|
124
|
+
'Try uploading to a different account if you have access to multiple',
|
|
125
|
+
`Working directory was: ${projectPath}`,
|
|
123
126
|
],
|
|
124
127
|
nextSteps: [
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
'1. Check authentication status: hs auth info',
|
|
129
|
+
'2. Validate project first: hs project validate',
|
|
130
|
+
'3. Review the error output above for specific issues',
|
|
131
|
+
'4. Try the upload command again after fixing issues',
|
|
129
132
|
],
|
|
130
133
|
};
|
|
131
134
|
}
|
|
132
135
|
}
|
|
133
136
|
catch (error) {
|
|
134
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
137
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
|
|
135
138
|
return {
|
|
136
|
-
status:
|
|
137
|
-
message:
|
|
139
|
+
status: 'error',
|
|
140
|
+
message: 'An error occurred during project upload',
|
|
138
141
|
error: errorMessage,
|
|
139
142
|
troubleshooting: [
|
|
140
143
|
"Ensure you're in a valid developer project directory",
|
|
141
|
-
|
|
142
|
-
|
|
144
|
+
'Check that hsproject.json exists in the project root',
|
|
145
|
+
'Verify HubSpot CLI is properly installed and accessible',
|
|
143
146
|
"Try running 'hs project upload' manually to see detailed output",
|
|
147
|
+
`Attempted project path: ${projectPath}`,
|
|
144
148
|
],
|
|
145
149
|
};
|
|
146
150
|
}
|
|
@@ -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. Defaults to current directory',
|
|
16
16
|
},
|
|
17
17
|
};
|
|
18
18
|
async execute(input) {
|
|
19
|
-
|
|
19
|
+
// Default to current working directory, which should be where Cursor is running from
|
|
20
|
+
const { autoFix = false, projectPath = process.cwd() } = 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,34 +70,35 @@ 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
|
}
|
|
101
104
|
return {
|
|
@@ -106,16 +109,22 @@ class ValidateProjectTool extends MCPTool {
|
|
|
106
109
|
}
|
|
107
110
|
else {
|
|
108
111
|
// Command failed to run
|
|
109
|
-
const errorMessage = result.error ||
|
|
112
|
+
const errorMessage = result.error || 'Failed to run project validation';
|
|
113
|
+
const errors = [errorMessage];
|
|
114
|
+
// Add debugging information about directory context
|
|
115
|
+
if (result.output.includes('Unable to locate a project configuration file')) {
|
|
116
|
+
errors.push(`Working directory was: ${projectPath}`);
|
|
117
|
+
errors.push("Make sure you're running this from a directory containing hsproject.json");
|
|
118
|
+
}
|
|
110
119
|
return {
|
|
111
120
|
isValid: false,
|
|
112
|
-
output: result.output ||
|
|
113
|
-
errors:
|
|
121
|
+
output: result.output || '',
|
|
122
|
+
errors: errors,
|
|
114
123
|
};
|
|
115
124
|
}
|
|
116
125
|
}
|
|
117
126
|
catch (error) {
|
|
118
|
-
const errorMessage = error instanceof Error ? error.message :
|
|
127
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown validation error';
|
|
119
128
|
throw new Error(`Failed to run project validation: ${errorMessage}`);
|
|
120
129
|
}
|
|
121
130
|
}
|
|
@@ -125,8 +134,8 @@ class ValidateProjectTool extends MCPTool {
|
|
|
125
134
|
}
|
|
126
135
|
parseValidationErrors(output) {
|
|
127
136
|
const errors = [];
|
|
128
|
-
const lines = output.split(
|
|
129
|
-
let currentFile =
|
|
137
|
+
const lines = output.split('\n');
|
|
138
|
+
let currentFile = '';
|
|
130
139
|
let inErrorSection = false;
|
|
131
140
|
for (const line of lines) {
|
|
132
141
|
// Look for file error headers like "Encountered the following errors for src/app/file.json:"
|
|
@@ -138,7 +147,7 @@ class ValidateProjectTool extends MCPTool {
|
|
|
138
147
|
}
|
|
139
148
|
// Parse individual error lines (start with tab and dash)
|
|
140
149
|
if (inErrorSection && line.match(/^\s*-\s+/)) {
|
|
141
|
-
const errorText = line.replace(/^\s*-\s+/,
|
|
150
|
+
const errorText = line.replace(/^\s*-\s+/, '').trim();
|
|
142
151
|
if (errorText) {
|
|
143
152
|
errors.push(`${currentFile}: ${errorText}`);
|
|
144
153
|
}
|
|
@@ -146,7 +155,7 @@ class ValidateProjectTool extends MCPTool {
|
|
|
146
155
|
}
|
|
147
156
|
// Handle multi-line errors with additional indentation
|
|
148
157
|
if (inErrorSection && line.match(/^\s{2,}-\s+/)) {
|
|
149
|
-
const errorText = line.replace(/^\s*-\s+/,
|
|
158
|
+
const errorText = line.replace(/^\s*-\s+/, '').trim();
|
|
150
159
|
if (errorText && errors.length > 0) {
|
|
151
160
|
// Append to the last error as additional context
|
|
152
161
|
errors[errors.length - 1] += `\n ${errorText}`;
|
|
@@ -154,10 +163,10 @@ class ValidateProjectTool extends MCPTool {
|
|
|
154
163
|
continue;
|
|
155
164
|
}
|
|
156
165
|
// Reset when we hit an empty line or new section
|
|
157
|
-
if (line.trim() ===
|
|
158
|
-
(!line.startsWith(
|
|
166
|
+
if (line.trim() === '' ||
|
|
167
|
+
(!line.startsWith('\t') && !line.startsWith(' '))) {
|
|
159
168
|
inErrorSection = false;
|
|
160
|
-
currentFile =
|
|
169
|
+
currentFile = '';
|
|
161
170
|
}
|
|
162
171
|
}
|
|
163
172
|
return errors;
|
|
@@ -165,37 +174,37 @@ class ValidateProjectTool extends MCPTool {
|
|
|
165
174
|
analyzeValidationErrors(errors) {
|
|
166
175
|
const analysis = [];
|
|
167
176
|
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 =
|
|
177
|
+
const filePath = error.split(':')[0];
|
|
178
|
+
const errorMessage = error.substring(error.indexOf(':') + 1).trim();
|
|
179
|
+
let category = 'unknown';
|
|
180
|
+
let component = 'unknown';
|
|
181
|
+
let severity = 'error';
|
|
173
182
|
// Extract file information
|
|
174
|
-
if (filePath.includes(
|
|
175
|
-
category =
|
|
176
|
-
component =
|
|
183
|
+
if (filePath.includes('hsproject.json')) {
|
|
184
|
+
category = 'project-config';
|
|
185
|
+
component = 'project';
|
|
177
186
|
}
|
|
178
|
-
else if (filePath.includes(
|
|
179
|
-
category =
|
|
187
|
+
else if (filePath.includes('-hsmeta.json')) {
|
|
188
|
+
category = 'component-config';
|
|
180
189
|
component = this.extractComponentFromPath(filePath);
|
|
181
190
|
}
|
|
182
|
-
else if (filePath.includes(
|
|
183
|
-
category =
|
|
191
|
+
else if (filePath.includes('package.json')) {
|
|
192
|
+
category = 'dependencies';
|
|
184
193
|
component = this.extractComponentFromPath(filePath);
|
|
185
|
-
severity =
|
|
194
|
+
severity = 'warning';
|
|
186
195
|
}
|
|
187
196
|
// Analyze error message content
|
|
188
|
-
if (errorMessage.includes(
|
|
189
|
-
category =
|
|
197
|
+
if (errorMessage.includes('Invalid JSON')) {
|
|
198
|
+
category = 'json-syntax';
|
|
190
199
|
}
|
|
191
|
-
else if (errorMessage.includes(
|
|
192
|
-
category =
|
|
200
|
+
else if (errorMessage.includes('Missing required field')) {
|
|
201
|
+
category = 'required-field';
|
|
193
202
|
}
|
|
194
|
-
else if (errorMessage.includes(
|
|
195
|
-
category =
|
|
203
|
+
else if (errorMessage.includes('must NOT have additional properties')) {
|
|
204
|
+
category = 'schema-validation';
|
|
196
205
|
}
|
|
197
|
-
else if (errorMessage.includes(
|
|
198
|
-
category =
|
|
206
|
+
else if (errorMessage.includes('entrypoint')) {
|
|
207
|
+
category = 'entrypoint';
|
|
199
208
|
}
|
|
200
209
|
analysis.push({
|
|
201
210
|
category: category,
|
|
@@ -209,46 +218,46 @@ class ValidateProjectTool extends MCPTool {
|
|
|
209
218
|
}
|
|
210
219
|
extractComponentFromPath(filePath) {
|
|
211
220
|
// Extract component type from file path like "src/app/cards/my-card-hsmeta.json"
|
|
212
|
-
if (filePath.includes(
|
|
213
|
-
const pathParts = filePath.split(
|
|
221
|
+
if (filePath.includes('/app/')) {
|
|
222
|
+
const pathParts = filePath.split('/app/')[1].split('/');
|
|
214
223
|
if (pathParts.length > 1) {
|
|
215
224
|
return pathParts[0]; // e.g., "cards", "functions", etc.
|
|
216
225
|
}
|
|
217
|
-
return
|
|
226
|
+
return 'app';
|
|
218
227
|
}
|
|
219
228
|
// Handle top-level app component files
|
|
220
|
-
if (filePath.includes(
|
|
221
|
-
return
|
|
229
|
+
if (filePath.includes('-hsmeta.json')) {
|
|
230
|
+
return 'app';
|
|
222
231
|
}
|
|
223
|
-
return
|
|
232
|
+
return 'unknown';
|
|
224
233
|
}
|
|
225
234
|
generateFixSuggestions(errorAnalysis) {
|
|
226
235
|
const suggestions = [];
|
|
227
236
|
for (const error of errorAnalysis) {
|
|
228
237
|
switch (error.category) {
|
|
229
|
-
case
|
|
230
|
-
suggestions.push("Fix hsproject.json: Ensure it contains 'name', 'srcDir', and 'platformVersion' fields",
|
|
238
|
+
case 'project-config':
|
|
239
|
+
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
240
|
break;
|
|
232
|
-
case
|
|
233
|
-
suggestions.push(`Fix ${error.component} component: Check the *-hsmeta.json file structure`,
|
|
241
|
+
case 'component-config':
|
|
242
|
+
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
243
|
break;
|
|
235
|
-
case
|
|
236
|
-
suggestions.push(`Fix JSON syntax error in ${error.file}`,
|
|
244
|
+
case 'json-syntax':
|
|
245
|
+
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
246
|
break;
|
|
238
|
-
case
|
|
239
|
-
suggestions.push(`Add missing required field: ${this.extractFieldFromError(error.issue)}`, `Update ${error.file} to include all required fields`,
|
|
247
|
+
case 'required-field':
|
|
248
|
+
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
249
|
break;
|
|
241
|
-
case
|
|
242
|
-
suggestions.push(`Fix schema validation error in ${error.file}`,
|
|
250
|
+
case 'schema-validation':
|
|
251
|
+
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
252
|
break;
|
|
244
|
-
case
|
|
245
|
-
suggestions.push(
|
|
253
|
+
case 'dependencies':
|
|
254
|
+
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
255
|
break;
|
|
247
|
-
case
|
|
248
|
-
suggestions.push(
|
|
256
|
+
case 'entrypoint':
|
|
257
|
+
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
258
|
break;
|
|
250
259
|
default:
|
|
251
|
-
suggestions.push(`Review error in ${error.file ||
|
|
260
|
+
suggestions.push(`Review error in ${error.file || 'project'}: ${error.issue}`);
|
|
252
261
|
}
|
|
253
262
|
}
|
|
254
263
|
return [...new Set(suggestions)]; // Remove duplicates
|
|
@@ -256,53 +265,53 @@ class ValidateProjectTool extends MCPTool {
|
|
|
256
265
|
extractFieldFromError(errorMessage) {
|
|
257
266
|
// Extract field name from messages like "Missing required field: config.description"
|
|
258
267
|
const match = errorMessage.match(/Missing required field:\s*(.+)/);
|
|
259
|
-
return match ? match[1] :
|
|
268
|
+
return match ? match[1] : 'unknown field';
|
|
260
269
|
}
|
|
261
270
|
generateNextSteps(errorAnalysis, autoFix) {
|
|
262
271
|
const steps = [];
|
|
263
272
|
if (autoFix) {
|
|
264
|
-
steps.push(
|
|
273
|
+
steps.push('1. Review auto-fix actions and apply them');
|
|
265
274
|
}
|
|
266
275
|
else {
|
|
267
|
-
steps.push(
|
|
276
|
+
steps.push('1. Review the validation errors and suggestions above');
|
|
268
277
|
}
|
|
269
|
-
const hasProjectConfig = errorAnalysis.some(
|
|
270
|
-
const hasComponentIssues = errorAnalysis.some(
|
|
271
|
-
const hasDependencyIssues = errorAnalysis.some(
|
|
278
|
+
const hasProjectConfig = errorAnalysis.some(e => e.category === 'project-config');
|
|
279
|
+
const hasComponentIssues = errorAnalysis.some(e => e.category === 'component-config');
|
|
280
|
+
const hasDependencyIssues = errorAnalysis.some(e => e.category === 'dependencies');
|
|
272
281
|
if (hasProjectConfig) {
|
|
273
|
-
steps.push(
|
|
282
|
+
steps.push('2. Fix hsproject.json configuration issues first');
|
|
274
283
|
}
|
|
275
284
|
if (hasComponentIssues) {
|
|
276
|
-
steps.push(
|
|
285
|
+
steps.push('3. Fix component configuration issues');
|
|
277
286
|
}
|
|
278
287
|
if (hasDependencyIssues) {
|
|
279
|
-
steps.push(
|
|
288
|
+
steps.push('4. Install missing dependencies');
|
|
280
289
|
}
|
|
281
|
-
steps.push(
|
|
282
|
-
steps.push(
|
|
290
|
+
steps.push('5. Run validation again to verify fixes');
|
|
291
|
+
steps.push('6. Test your components work correctly in development');
|
|
283
292
|
return steps;
|
|
284
293
|
}
|
|
285
294
|
generateAutoFixActions(errorAnalysis) {
|
|
286
295
|
const actions = [];
|
|
287
296
|
for (const error of errorAnalysis) {
|
|
288
297
|
switch (error.category) {
|
|
289
|
-
case
|
|
298
|
+
case 'project-config':
|
|
290
299
|
actions.push({
|
|
291
|
-
action:
|
|
292
|
-
description:
|
|
293
|
-
tool:
|
|
300
|
+
action: 'fix-project-config',
|
|
301
|
+
description: 'Generate valid hsproject.json configuration',
|
|
302
|
+
tool: 'generateProjectConfig',
|
|
294
303
|
});
|
|
295
304
|
break;
|
|
296
|
-
case
|
|
305
|
+
case 'dependencies':
|
|
297
306
|
actions.push({
|
|
298
|
-
action:
|
|
299
|
-
description:
|
|
300
|
-
command:
|
|
307
|
+
action: 'install-dependencies',
|
|
308
|
+
description: 'Install missing npm dependencies',
|
|
309
|
+
command: 'npm install',
|
|
301
310
|
});
|
|
302
311
|
break;
|
|
303
|
-
case
|
|
312
|
+
case 'component-config':
|
|
304
313
|
actions.push({
|
|
305
|
-
action:
|
|
314
|
+
action: 'fix-component-config',
|
|
306
315
|
description: `Fix ${error.component} component configuration`,
|
|
307
316
|
tool: `generate${error.component}Component`,
|
|
308
317
|
});
|
package/package.json
CHANGED