@hubspot/cli 7.7.15-experimental.0 → 7.7.16-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.
Files changed (71) hide show
  1. package/bin/cli.js +4 -0
  2. package/bin/hs +1 -1
  3. package/commands/getStarted.d.ts +9 -0
  4. package/commands/getStarted.js +227 -0
  5. package/commands/init.js +6 -1
  6. package/commands/mcp/setup.d.ts +4 -10
  7. package/commands/mcp/setup.js +28 -220
  8. package/commands/mcp/start.d.ts +3 -9
  9. package/commands/mcp/start.js +18 -15
  10. package/commands/mcp.js +2 -1
  11. package/commands/project/deploy.d.ts +1 -0
  12. package/commands/project/deploy.js +29 -3
  13. package/commands/project/upload.d.ts +2 -2
  14. package/commands/project/upload.js +18 -23
  15. package/commands/project/validate.d.ts +6 -0
  16. package/commands/project/validate.js +82 -0
  17. package/commands/project.js +2 -0
  18. package/commands/testAccount/create.d.ts +6 -0
  19. package/commands/testAccount/create.js +110 -0
  20. package/commands/testAccount/createConfig.d.ts +10 -0
  21. package/commands/testAccount/createConfig.js +98 -0
  22. package/commands/testAccount/delete.d.ts +6 -0
  23. package/commands/testAccount/delete.js +48 -0
  24. package/commands/testAccount.d.ts +3 -0
  25. package/commands/testAccount.js +28 -0
  26. package/lang/en.d.ts +144 -29
  27. package/lang/en.js +144 -32
  28. package/lang/en.lyaml +9 -14
  29. package/lib/app/migrate.js +15 -3
  30. package/lib/commonOpts.d.ts +2 -0
  31. package/lib/commonOpts.js +21 -9
  32. package/lib/constants.d.ts +5 -0
  33. package/lib/constants.js +6 -1
  34. package/lib/doctor/Doctor.js +1 -1
  35. package/lib/errorHandlers/index.js +7 -0
  36. package/lib/mcp/setup.d.ts +21 -0
  37. package/lib/mcp/setup.js +218 -0
  38. package/lib/projectProfiles.d.ts +1 -0
  39. package/lib/projectProfiles.js +18 -0
  40. package/lib/projects/buildAndDeploy.js +1 -1
  41. package/lib/projects/localDev/AppDevModeInterface.d.ts +3 -0
  42. package/lib/projects/localDev/AppDevModeInterface.js +45 -16
  43. package/lib/projects/localDev/LocalDevManager.js +1 -1
  44. package/lib/projects/upload.d.ts +3 -0
  45. package/lib/projects/upload.js +56 -22
  46. package/lib/projects/urls.d.ts +2 -0
  47. package/lib/projects/urls.js +10 -0
  48. package/lib/prompts/createDeveloperTestAccountConfigPrompt.d.ts +17 -0
  49. package/lib/prompts/createDeveloperTestAccountConfigPrompt.js +96 -0
  50. package/lib/prompts/installAppPrompt.d.ts +2 -1
  51. package/lib/prompts/installAppPrompt.js +12 -2
  52. package/lib/prompts/promptUtils.d.ts +1 -0
  53. package/lib/prompts/promptUtils.js +2 -0
  54. package/lib/ui/logger.d.ts +1 -0
  55. package/lib/ui/logger.js +1 -0
  56. package/lib/yargsUtils.d.ts +1 -0
  57. package/lib/yargsUtils.js +3 -0
  58. package/mcp-server/tools/index.js +2 -0
  59. package/mcp-server/tools/project/AddFeatureToProject.js +6 -29
  60. package/mcp-server/tools/project/CreateProjectTool.d.ts +3 -3
  61. package/mcp-server/tools/project/CreateProjectTool.js +12 -31
  62. package/mcp-server/tools/project/DeployProject.js +4 -11
  63. package/mcp-server/tools/project/GuidedWalkthroughTool.js +3 -16
  64. package/mcp-server/tools/project/UploadProjectTools.js +3 -7
  65. package/mcp-server/tools/project/ValidateProjectTool.d.ts +17 -0
  66. package/mcp-server/tools/project/ValidateProjectTool.js +35 -0
  67. package/mcp-server/utils/content.d.ts +3 -0
  68. package/mcp-server/utils/content.js +21 -0
  69. package/package.json +10 -9
  70. package/types/LocalDev.d.ts +1 -0
  71. package/types/Yargs.d.ts +4 -0
package/lib/commonOpts.js CHANGED
@@ -10,6 +10,7 @@ exports.addOverwriteOptions = addOverwriteOptions;
10
10
  exports.addCmsPublishModeOptions = addCmsPublishModeOptions;
11
11
  exports.addTestingOptions = addTestingOptions;
12
12
  exports.addUseEnvironmentOptions = addUseEnvironmentOptions;
13
+ exports.addJSONOutputOptions = addJSONOutputOptions;
13
14
  exports.addCustomHelpOutput = addCustomHelpOutput;
14
15
  exports.setLogLevel = setLogLevel;
15
16
  exports.getCommandName = getCommandName;
@@ -19,16 +20,16 @@ const yargs_parser_1 = __importDefault(require("yargs-parser"));
19
20
  const logger_1 = require("@hubspot/local-dev-lib/logger");
20
21
  const files_1 = require("@hubspot/local-dev-lib/constants/files");
21
22
  const config_1 = require("@hubspot/local-dev-lib/config");
22
- const lang_1 = require("./lang");
23
23
  const errorHandlers_1 = require("./errorHandlers");
24
24
  const exitCodes_1 = require("./enums/exitCodes");
25
25
  const ui_1 = require("./ui");
26
+ const lang_1 = require("./lang");
26
27
  function addGlobalOptions(yargs) {
27
28
  yargs.version(false);
28
29
  yargs.option('debug', {
29
30
  alias: 'd',
30
31
  default: false,
31
- describe: (0, lang_1.i18n)(`lib.commonOpts.options.debug.describe`),
32
+ describe: (0, lang_1.i18n)('lib.commonOpts.options.debug.describe'),
32
33
  type: 'boolean',
33
34
  });
34
35
  yargs.option('network-debug', {
@@ -41,21 +42,21 @@ function addGlobalOptions(yargs) {
41
42
  function addAccountOptions(yargs) {
42
43
  return yargs.option('account', {
43
44
  alias: 'a',
44
- describe: (0, lang_1.i18n)(`lib.commonOpts.options.account.describe`),
45
+ describe: (0, lang_1.i18n)('lib.commonOpts.options.account.describe'),
45
46
  type: 'string',
46
47
  });
47
48
  }
48
49
  function addConfigOptions(yargs) {
49
50
  return yargs.option('config', {
50
51
  alias: 'c',
51
- describe: (0, lang_1.i18n)(`lib.commonOpts.options.config.describe`),
52
+ describe: (0, lang_1.i18n)('lib.commonOpts.options.config.describe'),
52
53
  type: 'string',
53
54
  });
54
55
  }
55
56
  function addOverwriteOptions(yargs) {
56
57
  return yargs.option('overwrite', {
57
58
  alias: 'o',
58
- describe: (0, lang_1.i18n)(`lib.commonOpts.options.overwrite.describe`),
59
+ describe: (0, lang_1.i18n)('lib.commonOpts.options.overwrite.describe'),
59
60
  type: 'boolean',
60
61
  default: false,
61
62
  });
@@ -70,7 +71,7 @@ function addCmsPublishModeOptions(yargs, { read, write }) {
70
71
  }
71
72
  function addTestingOptions(yargs) {
72
73
  return yargs.option('qa', {
73
- describe: (0, lang_1.i18n)(`lib.commonOpts.options.qa.describe`),
74
+ describe: (0, lang_1.i18n)('lib.commonOpts.options.qa.describe'),
74
75
  type: 'boolean',
75
76
  default: false,
76
77
  hidden: true,
@@ -78,12 +79,20 @@ function addTestingOptions(yargs) {
78
79
  }
79
80
  function addUseEnvironmentOptions(yargs) {
80
81
  yargs.option('use-env', {
81
- describe: (0, lang_1.i18n)(`lib.commonOpts.options.useEnv.describe`),
82
+ describe: (0, lang_1.i18n)('lib.commonOpts.options.useEnv.describe'),
82
83
  type: 'boolean',
83
84
  });
84
85
  yargs.conflicts('use-env', 'account');
85
86
  return yargs;
86
87
  }
88
+ function addJSONOutputOptions(yargs) {
89
+ return yargs.option('json', {
90
+ alias: 'format-output-as-json',
91
+ describe: (0, lang_1.i18n)('lib.commonOpts.options.jsonOutput.describe'),
92
+ type: 'boolean',
93
+ hidden: true,
94
+ });
95
+ }
87
96
  async function addCustomHelpOutput(yargs, command, describe) {
88
97
  try {
89
98
  const parsedArgv = (0, yargs_parser_1.default)(process.argv.slice(2));
@@ -125,8 +134,11 @@ async function addCustomHelpOutput(yargs, command, describe) {
125
134
  }
126
135
  }
127
136
  function setLogLevel(options) {
128
- const { debug, networkDebug } = options;
129
- if (debug) {
137
+ const { debug, networkDebug, json } = options;
138
+ if (json) {
139
+ (0, logger_1.setLogLevel)(logger_1.LOG_LEVEL.NONE);
140
+ }
141
+ else if (debug) {
130
142
  (0, logger_1.setLogLevel)(logger_1.LOG_LEVEL.DEBUG);
131
143
  }
132
144
  else {
@@ -49,6 +49,7 @@ export declare const PROJECT_ERROR_TYPES: {
49
49
  readonly BUILD_NOT_IN_PROGRESS: "BuildPipelineErrorType.BUILD_NOT_IN_PROGRESS";
50
50
  readonly SUBBUILD_FAILED: "BuildPipelineErrorType.DEPENDENT_SUBBUILD_FAILED";
51
51
  readonly SUBDEPLOY_FAILED: "DeployPipelineErrorType.DEPENDENT_SUBDEPLOY_FAILED";
52
+ readonly DEPLOY_CONTAINS_REMOVALS: "DeployPipelineErrorType.WARNING_DEPLOY_CONTAINS_REMOVALS";
52
53
  };
53
54
  export declare const PROJECT_TASK_TYPES: {
54
55
  [key: string]: string;
@@ -98,6 +99,10 @@ export declare const oAuth = "oauth";
98
99
  export declare const privateDistribution = "private";
99
100
  export declare const marketplaceDistribution = "marketplace";
100
101
  export declare const appComponent = "app";
102
+ export declare const GET_STARTED_OPTIONS: {
103
+ readonly APP: "APP";
104
+ readonly CMS: "CMS";
105
+ };
101
106
  export declare const LOCAL_DEV_SERVER_MESSAGE_TYPES: {
102
107
  readonly INITIAL: "INITIAL";
103
108
  readonly WEBSOCKET_SERVER_CONNECTED: "WEBSOCKET_SERVER_CONNECTED";
package/lib/constants.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LOCAL_DEV_SERVER_MESSAGE_TYPES = exports.appComponent = exports.marketplaceDistribution = exports.privateDistribution = exports.oAuth = exports.staticAuth = exports.APP_INSTALLATION_STATES = exports.LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES = exports.LOCAL_DEV_UI_MESSAGE_SEND_TYPES = exports.FEATURES = exports.APP_AUTH_TYPES = exports.APP_DISTRIBUTION_TYPES = exports.IR_COMPONENT_TYPES = exports.PLATFORM_VERSION_ERROR_TYPES = exports.PROJECT_COMPONENT_TYPES = exports.PROJECT_TASK_TYPES = exports.PROJECT_ERROR_TYPES = exports.PROJECT_DEPLOY_TEXT = exports.PROJECT_BUILD_TEXT = exports.PROJECT_DEPLOY_STATES = exports.PROJECT_BUILD_STATES = exports.PROJECT_CONFIG_FILE = exports.DEFAULT_POLLING_DELAY = exports.MARKETPLACE_FOLDER = exports.HUBSPOT_FOLDER = exports.FEEDBACK_INTERVAL = exports.DEFAULT_PROJECT_TEMPLATE_BRANCH = exports.HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH = void 0;
3
+ exports.LOCAL_DEV_SERVER_MESSAGE_TYPES = exports.GET_STARTED_OPTIONS = exports.appComponent = exports.marketplaceDistribution = exports.privateDistribution = exports.oAuth = exports.staticAuth = exports.APP_INSTALLATION_STATES = exports.LOCAL_DEV_UI_MESSAGE_RECEIVE_TYPES = exports.LOCAL_DEV_UI_MESSAGE_SEND_TYPES = exports.FEATURES = exports.APP_AUTH_TYPES = exports.APP_DISTRIBUTION_TYPES = exports.IR_COMPONENT_TYPES = exports.PLATFORM_VERSION_ERROR_TYPES = exports.PROJECT_COMPONENT_TYPES = exports.PROJECT_TASK_TYPES = exports.PROJECT_ERROR_TYPES = exports.PROJECT_DEPLOY_TEXT = exports.PROJECT_BUILD_TEXT = exports.PROJECT_DEPLOY_STATES = exports.PROJECT_BUILD_STATES = exports.PROJECT_CONFIG_FILE = exports.DEFAULT_POLLING_DELAY = exports.MARKETPLACE_FOLDER = exports.HUBSPOT_FOLDER = exports.FEEDBACK_INTERVAL = exports.DEFAULT_PROJECT_TEMPLATE_BRANCH = exports.HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH = void 0;
4
4
  exports.HUBSPOT_PROJECT_COMPONENTS_GITHUB_PATH = 'HubSpot/hubspot-project-components';
5
5
  exports.DEFAULT_PROJECT_TEMPLATE_BRANCH = 'main';
6
6
  exports.FEEDBACK_INTERVAL = 10;
@@ -41,6 +41,7 @@ exports.PROJECT_ERROR_TYPES = {
41
41
  BUILD_NOT_IN_PROGRESS: 'BuildPipelineErrorType.BUILD_NOT_IN_PROGRESS',
42
42
  SUBBUILD_FAILED: 'BuildPipelineErrorType.DEPENDENT_SUBBUILD_FAILED',
43
43
  SUBDEPLOY_FAILED: 'DeployPipelineErrorType.DEPENDENT_SUBDEPLOY_FAILED',
44
+ DEPLOY_CONTAINS_REMOVALS: 'DeployPipelineErrorType.WARNING_DEPLOY_CONTAINS_REMOVALS',
44
45
  };
45
46
  exports.PROJECT_TASK_TYPES = {
46
47
  PRIVATE_APP: 'private app',
@@ -93,6 +94,10 @@ exports.oAuth = 'oauth';
93
94
  exports.privateDistribution = 'private';
94
95
  exports.marketplaceDistribution = 'marketplace';
95
96
  exports.appComponent = 'app';
97
+ exports.GET_STARTED_OPTIONS = {
98
+ APP: 'APP',
99
+ CMS: 'CMS',
100
+ };
96
101
  exports.LOCAL_DEV_SERVER_MESSAGE_TYPES = {
97
102
  INITIAL: 'INITIAL',
98
103
  WEBSOCKET_SERVER_CONNECTED: 'WEBSOCKET_SERVER_CONNECTED',
@@ -262,7 +262,7 @@ class Doctor {
262
262
  this.diagnosis?.addCliSection({
263
263
  type: 'success',
264
264
  message: i18n(`lib.doctor.hsChecks.latest`, {
265
- hsVersion: latestCLIVersion,
265
+ hsVersion: package_json_1.default.version,
266
266
  }),
267
267
  });
268
268
  }
@@ -15,6 +15,10 @@ const util_1 = __importDefault(require("util"));
15
15
  const ui_1 = require("../ui");
16
16
  function logError(error, context) {
17
17
  debugError(error, context);
18
+ if (isProjectValidationError(error)) {
19
+ logger_1.logger.error(error.message);
20
+ return;
21
+ }
18
22
  if ((0, suppressError_1.shouldSuppressError)(error, context)) {
19
23
  return;
20
24
  }
@@ -89,6 +93,9 @@ class ApiErrorContext {
89
93
  }
90
94
  }
91
95
  exports.ApiErrorContext = ApiErrorContext;
96
+ function isProjectValidationError(error) {
97
+ return error instanceof Error && error.name === 'ProjectValidationError';
98
+ }
92
99
  function isErrorWithMessageOrReason(error) {
93
100
  return (typeof error === 'object' &&
94
101
  error !== null &&
@@ -0,0 +1,21 @@
1
+ export declare const supportedTools: ({
2
+ name: "Claude Code";
3
+ value: string;
4
+ } | {
5
+ name: "Cursor";
6
+ value: string;
7
+ } | {
8
+ name: "Windsurf";
9
+ value: string;
10
+ })[];
11
+ interface McpCommand {
12
+ command: string;
13
+ args: string[];
14
+ }
15
+ export declare function addMintlifyMcpServer(installTargets: string[]): Promise<void>;
16
+ export declare function setupMintlify(derivedTargets?: string[]): Promise<boolean>;
17
+ export declare function addMcpServerToConfig(targets: string[] | undefined): Promise<string[]>;
18
+ export declare function setupClaudeCode(mcpCommand?: McpCommand): Promise<boolean>;
19
+ export declare function setupCursor(mcpCommand?: McpCommand): boolean;
20
+ export declare function setupWindsurf(mcpCommand?: McpCommand): boolean;
21
+ export {};
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.supportedTools = void 0;
7
+ exports.addMintlifyMcpServer = addMintlifyMcpServer;
8
+ exports.setupMintlify = setupMintlify;
9
+ exports.addMcpServerToConfig = addMcpServerToConfig;
10
+ exports.setupClaudeCode = setupClaudeCode;
11
+ exports.setupCursor = setupCursor;
12
+ exports.setupWindsurf = setupWindsurf;
13
+ const logger_1 = require("../ui/logger");
14
+ const en_1 = require("../../lang/en");
15
+ const promptUtils_1 = require("../prompts/promptUtils");
16
+ const SpinniesManager_1 = __importDefault(require("../ui/SpinniesManager"));
17
+ const errorHandlers_1 = require("../errorHandlers");
18
+ const command_1 = require("../../mcp-server/utils/command");
19
+ const child_process_1 = require("child_process");
20
+ const path_1 = __importDefault(require("path"));
21
+ const os_1 = __importDefault(require("os"));
22
+ const fs_extra_1 = __importDefault(require("fs-extra"));
23
+ const fs_1 = require("fs");
24
+ const mcpServerName = 'hubspot-cli-mcp';
25
+ const claudeCode = 'claude';
26
+ const windsurf = 'windsurf';
27
+ const cursor = 'cursor';
28
+ const supportedMintlifyClients = [windsurf, cursor];
29
+ exports.supportedTools = [
30
+ { name: en_1.commands.mcp.setup.claudeCode, value: claudeCode },
31
+ { name: en_1.commands.mcp.setup.cursor, value: cursor },
32
+ { name: en_1.commands.mcp.setup.windsurf, value: windsurf },
33
+ ];
34
+ const defaultMcpCommand = {
35
+ command: 'hs',
36
+ args: ['mcp', 'start'],
37
+ };
38
+ async function addMintlifyMcpServer(installTargets) {
39
+ await runSetupFunction(() => setupMintlify(installTargets));
40
+ }
41
+ async function setupMintlify(derivedTargets = supportedMintlifyClients) {
42
+ logger_1.uiLogger.info(en_1.commands.mcp.setup.installingDocSearch);
43
+ logger_1.uiLogger.log('');
44
+ return new Promise(resolve => {
45
+ const subcommands = ['mint-mcp', 'add', 'hubspot-migration'];
46
+ const docsSearchClients = derivedTargets.filter(target => supportedMintlifyClients.includes(target));
47
+ const childProcess = (0, child_process_1.spawn)(`npx`, docsSearchClients && docsSearchClients.length
48
+ ? [...subcommands, '--client', ...docsSearchClients]
49
+ : subcommands, {
50
+ stdio: 'inherit',
51
+ });
52
+ childProcess.on('exit', code => {
53
+ if (code !== 0) {
54
+ resolve(false);
55
+ }
56
+ resolve(true);
57
+ });
58
+ });
59
+ }
60
+ async function addMcpServerToConfig(targets) {
61
+ try {
62
+ let derivedTargets = [];
63
+ if (!targets || targets.length === 0) {
64
+ const { selectedTargets } = await (0, promptUtils_1.promptUser)({
65
+ name: 'selectedTargets',
66
+ type: 'checkbox',
67
+ message: en_1.commands.mcp.setup.prompts.targets,
68
+ choices: exports.supportedTools,
69
+ validate: (choices) => {
70
+ return choices.length === 0
71
+ ? en_1.commands.mcp.setup.prompts.targetsRequired
72
+ : true;
73
+ },
74
+ });
75
+ derivedTargets = selectedTargets;
76
+ }
77
+ else {
78
+ derivedTargets = targets;
79
+ }
80
+ SpinniesManager_1.default.init();
81
+ if (derivedTargets.includes(claudeCode)) {
82
+ await runSetupFunction(setupClaudeCode);
83
+ }
84
+ if (derivedTargets.includes(cursor)) {
85
+ await runSetupFunction(setupCursor);
86
+ }
87
+ if (derivedTargets.includes(windsurf)) {
88
+ await runSetupFunction(setupWindsurf);
89
+ }
90
+ logger_1.uiLogger.info(en_1.commands.mcp.setup.success(derivedTargets));
91
+ return derivedTargets;
92
+ }
93
+ catch (error) {
94
+ SpinniesManager_1.default.fail('mcpSetup', {
95
+ text: en_1.commands.mcp.setup.spinners.failedToConfigure,
96
+ });
97
+ throw error;
98
+ }
99
+ }
100
+ async function runSetupFunction(func) {
101
+ const result = await func();
102
+ if (!result) {
103
+ throw new Error();
104
+ }
105
+ }
106
+ function setupMcpConfigFile(config) {
107
+ try {
108
+ SpinniesManager_1.default.add('spinner', {
109
+ text: config.configuringMessage,
110
+ });
111
+ if (!(0, fs_1.existsSync)(config.configPath)) {
112
+ fs_extra_1.default.writeFileSync(config.configPath, JSON.stringify({}, null, 2));
113
+ }
114
+ let mcpConfig = {};
115
+ try {
116
+ const configContent = fs_extra_1.default.readFileSync(config.configPath, 'utf8');
117
+ mcpConfig = JSON.parse(configContent);
118
+ }
119
+ catch (error) {
120
+ SpinniesManager_1.default.fail('spinner', {
121
+ text: config.failedMessage,
122
+ });
123
+ (0, errorHandlers_1.logError)(error);
124
+ return false;
125
+ }
126
+ // Initialize mcpServers if it doesn't exist
127
+ if (!mcpConfig.mcpServers) {
128
+ mcpConfig.mcpServers = {};
129
+ }
130
+ // Add or update HubSpot CLI MCP server
131
+ mcpConfig.mcpServers[mcpServerName] = {
132
+ ...config.mcpCommand,
133
+ };
134
+ // Write the updated config
135
+ fs_extra_1.default.writeFileSync(config.configPath, JSON.stringify(mcpConfig, null, 2));
136
+ SpinniesManager_1.default.succeed('spinner', {
137
+ text: config.configuredMessage,
138
+ });
139
+ return true;
140
+ }
141
+ catch (error) {
142
+ SpinniesManager_1.default.fail('spinner', {
143
+ text: config.failedMessage,
144
+ });
145
+ (0, errorHandlers_1.logError)(error);
146
+ return false;
147
+ }
148
+ }
149
+ async function setupClaudeCode(mcpCommand = defaultMcpCommand) {
150
+ try {
151
+ SpinniesManager_1.default.add('claudeCode', {
152
+ text: en_1.commands.mcp.setup.spinners.configuringClaudeCode,
153
+ });
154
+ try {
155
+ // Check if claude command is available
156
+ await (0, command_1.execAsync)('claude --version');
157
+ // Run claude mcp add command
158
+ const mcpConfig = JSON.stringify({
159
+ type: 'stdio',
160
+ ...mcpCommand,
161
+ });
162
+ const { stdout } = await (0, command_1.execAsync)('claude mcp list');
163
+ if (stdout.includes(mcpServerName)) {
164
+ SpinniesManager_1.default.update('claudeCode', {
165
+ text: en_1.commands.mcp.setup.spinners.alreadyInstalled,
166
+ });
167
+ await (0, command_1.execAsync)(`claude mcp remove "${mcpServerName}" --scope user`);
168
+ }
169
+ await (0, command_1.execAsync)(`claude mcp add-json "${mcpServerName}" '${mcpConfig}' --scope user`);
170
+ SpinniesManager_1.default.succeed('claudeCode', {
171
+ text: en_1.commands.mcp.setup.spinners.configuredClaudeCode,
172
+ });
173
+ return true;
174
+ }
175
+ catch (error) {
176
+ if (error instanceof Error &&
177
+ error.message.includes('claude: command not found')) {
178
+ SpinniesManager_1.default.fail('claudeCode', {
179
+ text: en_1.commands.mcp.setup.spinners.claudeCodeNotFound,
180
+ });
181
+ }
182
+ else {
183
+ SpinniesManager_1.default.fail('claudeCode', {
184
+ text: en_1.commands.mcp.setup.spinners.claudeCodeInstallFailed,
185
+ });
186
+ (0, errorHandlers_1.logError)(error);
187
+ }
188
+ return false;
189
+ }
190
+ }
191
+ catch (error) {
192
+ SpinniesManager_1.default.fail('claudeCode', {
193
+ text: en_1.commands.mcp.setup.spinners.claudeCodeInstallFailed,
194
+ });
195
+ (0, errorHandlers_1.logError)(error);
196
+ return false;
197
+ }
198
+ }
199
+ function setupCursor(mcpCommand = defaultMcpCommand) {
200
+ const cursorConfigPath = path_1.default.join(os_1.default.homedir(), '.cursor', 'mcp.json');
201
+ return setupMcpConfigFile({
202
+ configPath: cursorConfigPath,
203
+ configuringMessage: en_1.commands.mcp.setup.spinners.configuringCursor,
204
+ configuredMessage: en_1.commands.mcp.setup.spinners.configuredCursor,
205
+ failedMessage: en_1.commands.mcp.setup.spinners.failedToConfigureCursor,
206
+ mcpCommand,
207
+ });
208
+ }
209
+ function setupWindsurf(mcpCommand = defaultMcpCommand) {
210
+ const windsurfConfigPath = path_1.default.join(os_1.default.homedir(), '.codeium', 'windsurf', 'mcp_config.json');
211
+ return setupMcpConfigFile({
212
+ configPath: windsurfConfigPath,
213
+ configuringMessage: en_1.commands.mcp.setup.spinners.configuringWindsurf,
214
+ configuredMessage: en_1.commands.mcp.setup.spinners.configuredWindsurf,
215
+ failedMessage: en_1.commands.mcp.setup.spinners.failedToConfigureWindsurf,
216
+ mcpCommand,
217
+ });
218
+ }
@@ -4,3 +4,4 @@ export declare function logProfileHeader(profileName: string): void;
4
4
  export declare function logProfileFooter(profile: HsProfileFile, includeVariables?: boolean): void;
5
5
  export declare function loadProfile(projectConfig: ProjectConfig | null, projectDir: string | null, profileName: string): HsProfileFile | undefined;
6
6
  export declare function exitIfUsingProfiles(projectConfig: ProjectConfig | null, projectDir: string | null): Promise<void>;
7
+ export declare function loadAndValidateProfile(projectConfig: ProjectConfig | null, projectDir: string | null, argsProfile: string | undefined): Promise<number | undefined>;
@@ -7,6 +7,7 @@ exports.logProfileHeader = logProfileHeader;
7
7
  exports.logProfileFooter = logProfileFooter;
8
8
  exports.loadProfile = loadProfile;
9
9
  exports.exitIfUsingProfiles = exitIfUsingProfiles;
10
+ exports.loadAndValidateProfile = loadAndValidateProfile;
10
11
  const path_1 = __importDefault(require("path"));
11
12
  const project_parsing_lib_1 = require("@hubspot/project-parsing-lib");
12
13
  const en_1 = require("../lang/en");
@@ -63,3 +64,20 @@ async function exitIfUsingProfiles(projectConfig, projectDir) {
63
64
  }
64
65
  }
65
66
  }
67
+ async function loadAndValidateProfile(projectConfig, projectDir, argsProfile) {
68
+ if (argsProfile) {
69
+ logProfileHeader(argsProfile);
70
+ const profile = loadProfile(projectConfig, projectDir, argsProfile);
71
+ if (!profile) {
72
+ (0, ui_1.uiLine)();
73
+ process.exit(exitCodes_1.EXIT_CODES.ERROR);
74
+ }
75
+ logProfileFooter(profile, true);
76
+ return profile.accountId;
77
+ }
78
+ else {
79
+ // A profile must be specified if this project has profiles configured
80
+ await exitIfUsingProfiles(projectConfig, projectDir);
81
+ }
82
+ return undefined;
83
+ }
@@ -228,7 +228,7 @@ function makePollTaskStatusFunc({ statusFn, structureFn, statusText, statusStrin
228
228
  }
229
229
  function pollBuildAutodeployStatus(accountId, taskName, buildId) {
230
230
  return new Promise((resolve, reject) => {
231
- let maxIntervals = (30 * 1000) / constants_1.DEFAULT_POLLING_DELAY; // Num of intervals in ~30s
231
+ let maxIntervals = (15 * 1000) / constants_1.DEFAULT_POLLING_DELAY; // Num of intervals in ~15s
232
232
  const pollInterval = setInterval(async () => {
233
233
  let build;
234
234
  try {
@@ -14,9 +14,12 @@ declare class AppDevModeInterface {
14
14
  private get appNode();
15
15
  private get appData();
16
16
  private set appData(value);
17
+ private isAutomaticallyInstallable;
17
18
  private getAppInstallUrl;
18
19
  private fetchAppData;
19
20
  private checkMarketplaceAppInstalls;
21
+ private autoInstallStaticAuthApp;
22
+ private installAppOrOpenInstallUrl;
20
23
  private checkTestAccountAppInstallation;
21
24
  private setUpLocalDevServerMessageListeners;
22
25
  setup(args: any): Promise<void>;
@@ -4,6 +4,7 @@ const localDevAuth_1 = require("@hubspot/local-dev-lib/api/localDevAuth");
4
4
  const appsDev_1 = require("@hubspot/local-dev-lib/api/appsDev");
5
5
  const ui_extensions_dev_server_1 = require("@hubspot/ui-extensions-dev-server");
6
6
  const portManager_1 = require("@hubspot/local-dev-lib/portManager");
7
+ const config_1 = require("@hubspot/local-dev-lib/config");
7
8
  const constants_1 = require("../../constants");
8
9
  const exitCodes_1 = require("../../enums/exitCodes");
9
10
  const structure_1 = require("../../projects/structure");
@@ -14,6 +15,7 @@ const promptUtils_1 = require("../../prompts/promptUtils");
14
15
  const en_1 = require("../../../lang/en");
15
16
  const logger_1 = require("../../ui/logger");
16
17
  const urls_1 = require("../../app/urls");
18
+ const accountTypes_1 = require("../../accountTypes");
17
19
  class AppDevModeInterface {
18
20
  localDevState;
19
21
  localDevLogger;
@@ -22,15 +24,6 @@ class AppDevModeInterface {
22
24
  constructor(options) {
23
25
  this.localDevState = options.localDevState;
24
26
  this.localDevLogger = options.localDevLogger;
25
- // Static auth apps are currently only installable in the portal that the project resides in
26
- // This limitation will eventually be removed, but in the meantime we need this check or the install
27
- // will always fail with a confusing message
28
- if (this.appNode?.config.auth.type === constants_1.APP_AUTH_TYPES.STATIC &&
29
- this.localDevState.targetTestingAccountId !==
30
- this.localDevState.targetProjectAccountId) {
31
- logger_1.uiLogger.error(en_1.lib.LocalDevManager.staticAuthAccountsMustMatch);
32
- process.exit(exitCodes_1.EXIT_CODES.ERROR);
33
- }
34
27
  if (!this.localDevState.targetProjectAccountId ||
35
28
  !this.localDevState.projectConfig ||
36
29
  !this.localDevState.projectDir) {
@@ -59,6 +52,19 @@ class AppDevModeInterface {
59
52
  }
60
53
  this.localDevState.setAppDataForUid(this.appNode.uid, appData);
61
54
  }
55
+ isAutomaticallyInstallable() {
56
+ const targetTestingAccount = (0, config_1.getAccountConfig)(this.localDevState.targetTestingAccountId);
57
+ if (!targetTestingAccount) {
58
+ return false;
59
+ }
60
+ const isTestAccount = (0, accountTypes_1.isDeveloperTestAccount)(targetTestingAccount) ||
61
+ (0, accountTypes_1.isSandbox)(targetTestingAccount);
62
+ const hasCorrectParent = targetTestingAccount.parentAccountId ===
63
+ this.localDevState.targetProjectAccountId;
64
+ return (isTestAccount &&
65
+ hasCorrectParent &&
66
+ this.appNode?.config.auth.type === constants_1.APP_AUTH_TYPES.STATIC);
67
+ }
62
68
  async getAppInstallUrl() {
63
69
  if (this.appNode?.config.auth.type === constants_1.APP_AUTH_TYPES.OAUTH) {
64
70
  return (0, urls_1.getOauthAppInstallUrl)({
@@ -72,7 +78,7 @@ class AppDevModeInterface {
72
78
  const { data: { results }, } = await (0, appsDev_1.fetchPublicAppsForPortal)(this.localDevState.targetProjectAccountId);
73
79
  const app = results.find(app => app.sourceId === this.appNode?.uid);
74
80
  if (!app) {
75
- logger_1.uiLogger.error(en_1.lib.LocalDevManager.staticAuthAccountsMustMatch);
81
+ logger_1.uiLogger.error(en_1.lib.LocalDevManager.appNotFound(this.localDevState.targetProjectAccountId, this.appNode?.uid));
76
82
  process.exit(exitCodes_1.EXIT_CODES.ERROR);
77
83
  }
78
84
  return (0, urls_1.getStaticAuthAppInstallUrl)({
@@ -93,6 +99,7 @@ class AppDevModeInterface {
93
99
  clientId: appData.clientId,
94
100
  name: appData.name,
95
101
  installationState: constants_1.APP_INSTALLATION_STATES.NOT_INSTALLED,
102
+ scopeGroupIds: appData.scopeGroupIds,
96
103
  };
97
104
  this.marketplaceAppInstalls = uniquePortalInstallCount;
98
105
  }
@@ -110,9 +117,31 @@ class AppDevModeInterface {
110
117
  }
111
118
  this.localDevLogger.addUploadWarning(en_1.lib.AppDevModeInterface.defaultMarketplaceAppWarning(this.marketplaceAppInstalls));
112
119
  }
120
+ async autoInstallStaticAuthApp() {
121
+ const shouldInstall = await (0, installAppPrompt_1.installAppAutoPrompt)();
122
+ if (!shouldInstall) {
123
+ logger_1.uiLogger.log(en_1.lib.AppDevModeInterface.autoInstallDeclined);
124
+ process.exit(exitCodes_1.EXIT_CODES.SUCCESS);
125
+ }
126
+ await (0, appsDev_1.installStaticAuthAppOnTestAccount)(this.appData.id, this.localDevState.targetTestingAccountId, this.appData.scopeGroupIds);
127
+ }
128
+ async installAppOrOpenInstallUrl(isReinstall) {
129
+ if (this.isAutomaticallyInstallable()) {
130
+ try {
131
+ await this.autoInstallStaticAuthApp();
132
+ logger_1.uiLogger.success(en_1.lib.AppDevModeInterface.autoInstallSuccess(this.appData.name, this.localDevState.targetTestingAccountId));
133
+ return;
134
+ }
135
+ catch (e) {
136
+ logger_1.uiLogger.error(en_1.lib.AppDevModeInterface.autoInstallError(this.appData.name, this.localDevState.targetTestingAccountId));
137
+ }
138
+ }
139
+ const installUrl = await this.getAppInstallUrl();
140
+ await (0, installAppPrompt_1.installAppBrowserPrompt)(installUrl, isReinstall);
141
+ }
113
142
  async checkTestAccountAppInstallation() {
114
143
  if (!this.appNode || !this.appData) {
115
- return;
144
+ return {};
116
145
  }
117
146
  const { data: { isInstalledWithScopeGroups, previouslyAuthorizedScopeGroups }, } = await (0, localDevAuth_1.fetchAppInstallationData)(this.localDevState.targetTestingAccountId, this.localDevState.projectId, this.appNode.uid, this.appNode.config.auth.requiredScopes, this.appNode.config.auth.optionalScopes);
118
147
  const isReinstall = previouslyAuthorizedScopeGroups.length > 0;
@@ -128,10 +157,7 @@ class AppDevModeInterface {
128
157
  installationState: constants_1.APP_INSTALLATION_STATES.INSTALLED_WITH_OUTDATED_SCOPES,
129
158
  };
130
159
  }
131
- if (!isInstalledWithScopeGroups) {
132
- const installUrl = await this.getAppInstallUrl();
133
- await (0, installAppPrompt_1.installAppPrompt)(installUrl, isReinstall);
134
- }
160
+ return { needsInstall: !isInstalledWithScopeGroups, isReinstall };
135
161
  }
136
162
  setUpLocalDevServerMessageListeners() {
137
163
  this.localDevState.addListener('devServerMessage', message => {
@@ -151,7 +177,10 @@ class AppDevModeInterface {
151
177
  if (this.appNode.config.distribution === constants_1.APP_DISTRIBUTION_TYPES.MARKETPLACE) {
152
178
  await this.checkMarketplaceAppInstalls();
153
179
  }
154
- await this.checkTestAccountAppInstallation();
180
+ const { needsInstall, isReinstall } = await this.checkTestAccountAppInstallation();
181
+ if (needsInstall) {
182
+ await this.installAppOrOpenInstallUrl(isReinstall || false);
183
+ }
155
184
  }
156
185
  catch (e) {
157
186
  (0, index_1.logError)(e);
@@ -195,7 +195,7 @@ class LocalDevManager {
195
195
  scopes: this.activeApp.config.auth.requiredScopes,
196
196
  redirectUrls: this.activeApp.config.auth.redirectUrls,
197
197
  });
198
- await (0, installAppPrompt_1.installAppPrompt)(installUrl, isReinstall);
198
+ await (0, installAppPrompt_1.installAppBrowserPrompt)(installUrl, isReinstall);
199
199
  }
200
200
  }
201
201
  updateKeypressListeners() {
@@ -18,4 +18,7 @@ type HandleProjectUploadArg<T> = {
18
18
  profile?: string;
19
19
  };
20
20
  export declare function handleProjectUpload<T>({ accountId, projectConfig, projectDir, callbackFunc, profile, uploadMessage, forceCreate, isUploadCommand, sendIR, skipValidation, }: HandleProjectUploadArg<T>): Promise<ProjectUploadResult<T>>;
21
+ export declare function validateSourceDirectory(srcDir: string, projectConfig: ProjectConfig): void;
22
+ export declare function validateNoHSMetaMismatch(srcDir: string, projectConfig: ProjectConfig): Promise<void>;
23
+ export declare function handleTranslate(projectDir: string, projectConfig: ProjectConfig, accountId: number, skipValidation: boolean, profile: string | undefined): Promise<unknown>;
21
24
  export {};